} versions The versions to check.
+ */
+ _validateVersionSyntax(versions) {
+ for (const version of versions) {
+ if (!semver.valid(version)) {
+ throw version;
+ }
+ }
+ }
+
+ /**
+ * Runs the check.
+ */
+ async check() {
+ const core = await import('@actions/core');
+ /* eslint-disable no-console */
+ try {
+ console.log(`\nChecking ${this.packageName} versions in CI environments...`);
+
+ // Validate released versions syntax
+ try {
+ this._validateVersionSyntax(this.releasedVersions);
+ } catch (e) {
+ core.setFailed(`Failed to check ${this.packageName} versions because released version '${e}' does not follow semver syntax (x.y.z).`);
+ return;
+ }
+
+ // Sort versions descending
+ semver.sort(this.releasedVersions).reverse()
+
+ // Get tested package versions from CI
+ const tests = await this.getTests();
+
+ // Is true if any of the checks failed
+ let failed = false;
+
+ // Check whether each tested version is the latest patch
+ for (const test of tests) {
+ const version = test[this.ciVersionKey];
+
+ // Validate version syntax
+ try {
+ this._validateVersionSyntax([version]);
+ } catch (e) {
+ core.setFailed(`Failed to check ${this.packageName} versions because environment version '${e}' does not follow semver syntax (x.y.z).`);
+ return;
+ }
+
+ const newer = this.getNewerVersion(this.releasedVersions, version, this.latestComponent);
+ if (newer) {
+ console.log(`â CI environment '${test.name}' uses an old ${this.packageName} ${this.latestComponent} version ${version} instead of ${newer}.`);
+ failed = true;
+ } else {
+ console.log(`â
CI environment '${test.name}' uses the latest ${this.packageName} ${this.latestComponent} version ${version}.`);
+ }
+ }
+
+ // Check whether there is a newer component version available that is not tested
+ const testedVersions = tests.map(test => test[this.ciVersionKey]);
+ const untested = this.getUntestedVersions(this.releasedVersions, testedVersions, this.latestComponent);
+ if (untested.length > 0) {
+ console.log(`â CI does not have environments using the following versions of ${this.packageName}: ${untested.join(', ')}.`);
+ failed = true;
+ } else {
+ console.log(`â
CI has environments using all recent versions of ${this.packageName}.`);
+ }
+
+ if (failed) {
+ core.setFailed(
+ `CI environments are not up-to-date with the latest ${this.packageName} versions.` +
+ `\n\nCheck the error messages above and update the ${this.packageName} versions in the CI YAML ` +
+ `file.\n\nâšī¸ Additionally, there may be versions of ${this.packageName} that have reached their official end-of-life ` +
+ `support date and should be removed from the CI, see ${this.packageSupportUrl}.`
+ );
+ }
+
+ } catch (e) {
+ const msg = `Failed to check ${this.packageName} versions with error: ${e}`;
+ core.setFailed(msg);
+ }
+ /* eslint-enable no-console */
+ }
+}
+
+module.exports = CiVersionCheck;
diff --git a/ci/ciCheck.js b/ci/ciCheck.js
new file mode 100644
index 0000000000..8ee58cbdfd
--- /dev/null
+++ b/ci/ciCheck.js
@@ -0,0 +1,69 @@
+'use strict';
+
+const CiVersionCheck = require('./CiVersionCheck');
+const { exec } = require('child_process');
+
+async function check() {
+ // Run checks
+ await checkMongoDbVersions();
+ await checkNodeVersions();
+}
+
+/**
+ * Check the MongoDB versions used in test environments.
+ */
+async function checkMongoDbVersions() {
+ let latestStableVersions = await new Promise((resolve, reject) => {
+ exec('m ls', (error, stdout) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve(stdout.trim());
+ });
+ });
+ latestStableVersions = latestStableVersions.split('\n').map(version => version.trim());
+
+ await new CiVersionCheck({
+ packageName: 'MongoDB',
+ packageSupportUrl: 'https://www.mongodb.com/support-policy',
+ yamlFilePath: './.github/workflows/ci.yml',
+ ciEnvironmentsKeyPath: 'jobs.check-mongo.strategy.matrix.include',
+ ciVersionKey: 'MONGODB_VERSION',
+ releasedVersions: latestStableVersions,
+ latestComponent: CiVersionCheck.versionComponents.patch,
+ ignoreReleasedVersions: [
+ '<4.2.0', // These versions have reached their end-of-life support date
+ '>=4.3.0 <5.0.0', // Unsupported rapid release versions
+ '>=5.1.0 <6.0.0', // Unsupported rapid release versions
+ '>=6.1.0 <7.0.0', // Unsupported rapid release versions
+ '>=7.1.0 <8.0.0', // Unsupported rapid release versions
+ ],
+ }).check();
+}
+
+/**
+ * Check the Nodejs versions used in test environments.
+ */
+async function checkNodeVersions() {
+ const allVersions = (await import('all-node-versions')).default;
+ const { versions } = await allVersions();
+ const nodeVersions = versions.map(version => version.node);
+
+ await new CiVersionCheck({
+ packageName: 'Node.js',
+ packageSupportUrl: 'https://github.com/nodejs/node/blob/master/CHANGELOG.md',
+ yamlFilePath: './.github/workflows/ci.yml',
+ ciEnvironmentsKeyPath: 'jobs.check-mongo.strategy.matrix.include',
+ ciVersionKey: 'NODE_VERSION',
+ releasedVersions: nodeVersions,
+ latestComponent: CiVersionCheck.versionComponents.minor,
+ ignoreReleasedVersions: [
+ '<18.0.0', // These versions have reached their end-of-life support date
+ '>=19.0.0 <20.0.0', // These versions have reached their end-of-life support date
+ '>=21.0.0', // These versions are not officially supported yet
+ ],
+ }).check();
+}
+
+check();
diff --git a/ci/definitionsCheck.js b/ci/definitionsCheck.js
new file mode 100644
index 0000000000..b4b9e88d0a
--- /dev/null
+++ b/ci/definitionsCheck.js
@@ -0,0 +1,27 @@
+const fs = require('fs').promises;
+const { exec } = require('child_process');
+const util = require('util');
+(async () => {
+ const core = await import('@actions/core');
+ const [currentDefinitions, currentDocs] = await Promise.all([
+ fs.readFile('./src/Options/Definitions.js', 'utf8'),
+ fs.readFile('./src/Options/docs.js', 'utf8'),
+ ]);
+ const execute = util.promisify(exec);
+ await execute('npm run definitions');
+ const [newDefinitions, newDocs] = await Promise.all([
+ fs.readFile('./src/Options/Definitions.js', 'utf8'),
+ fs.readFile('./src/Options/docs.js', 'utf8'),
+ ]);
+ if (currentDefinitions !== newDefinitions || currentDocs !== newDocs) {
+ // eslint-disable-next-line no-console
+ console.error(
+ '\x1b[31m%s\x1b[0m',
+ 'Definitions files cannot be updated manually. Please update src/Options/index.js then run `npm run definitions` to generate definitions.'
+ );
+ core.error('Definitions files cannot be updated manually. Please update src/Options/index.js then run `npm run definitions` to generate definitions.');
+ process.exit(1);
+ } else {
+ process.exit(0);
+ }
+})();
diff --git a/ci/nodeEngineCheck.js b/ci/nodeEngineCheck.js
new file mode 100644
index 0000000000..e2c4553604
--- /dev/null
+++ b/ci/nodeEngineCheck.js
@@ -0,0 +1,198 @@
+const semver = require('semver');
+const fs = require('fs').promises;
+const path = require('path');
+let core;
+
+/**
+ * This checks whether any package dependency requires a minimum node engine
+ * version higher than the host package.
+ */
+class NodeEngineCheck {
+
+ /**
+ * The constructor.
+ * @param {Object} config The config.
+ * @param {String} config.nodeModulesPath The path to the node_modules directory.
+ * @param {String} config.packageJsonPath The path to the parent package.json file.
+ */
+ constructor(config) {
+ const {
+ nodeModulesPath,
+ packageJsonPath,
+ } = config;
+
+ // Ensure required params are set
+ if ([
+ nodeModulesPath,
+ packageJsonPath,
+ ].includes(undefined)) {
+ throw 'invalid configuration';
+ }
+
+ this.nodeModulesPath = nodeModulesPath;
+ this.packageJsonPath = packageJsonPath;
+ }
+
+ /**
+ * Returns an array of `package.json` files under the given path and subdirectories.
+ * @param {String} [basePath] The base path for recursive directory search.
+ */
+ async getPackageFiles(basePath = this.nodeModulesPath) {
+ try {
+ // Declare file list
+ const files = []
+
+ // Get files
+ const dirents = await fs.readdir(basePath, { withFileTypes: true });
+ const validFiles = dirents.filter(d => d.name.toLowerCase() == 'package.json').map(d => path.join(basePath, d.name));
+ files.push(...validFiles);
+
+ // For each directory entry
+ for (const dirent of dirents) {
+ if (dirent.isDirectory()) {
+ const subFiles = await this.getPackageFiles(path.join(basePath, dirent.name));
+ files.push(...subFiles);
+ }
+ }
+ return files;
+ } catch (e) {
+ throw `Failed to get package.json files in ${this.nodeModulesPath} with error: ${e}`;
+ }
+ }
+
+ /**
+ * Extracts and returns the node engine versions of the given package.json
+ * files.
+ * @param {String[]} files The package.json files.
+ * @param {Boolean} clean Is true if packages with undefined node versions
+ * should be removed from the results.
+ * @returns {Object[]} A list of results.
+ */
+ async getNodeVersion({ files, clean = false }) {
+
+ // Declare response
+ let response = [];
+
+ // For each file
+ for (const file of files) {
+ // Get node version
+ const contentString = await fs.readFile(file, 'utf-8');
+ try {
+ const contentJson = JSON.parse(contentString);
+ const version = ((contentJson || {}).engines || {}).node;
+
+ // Add response
+ response.push({
+ file: file,
+ nodeVersion: version
+ });
+ } catch {
+ // eslint-disable-next-line no-console
+ console.log(`Ignoring file because it is not valid JSON: ${file}`);
+ core.warning(`Ignoring file because it is not valid JSON: ${file}`);
+ }
+ }
+
+ // If results should be cleaned by removing undefined node versions
+ if (clean) {
+ response = response.filter(r => r.nodeVersion !== undefined);
+ }
+ return response;
+ }
+
+ /**
+ * Returns the highest semver definition that satisfies all versions
+ * in the given list.
+ * @param {String[]} versions The list of semver version ranges.
+ * @param {String} baseVersion The base version of which higher versions should be
+ * determined; as a version (1.2.3), not a range (>=1.2.3).
+ * @returns {String} The highest semver version.
+ */
+ getHigherVersions({ versions, baseVersion }) {
+ // Add min satisfying node versions
+ const minVersions = versions.map(v => {
+ v.nodeMinVersion = semver.minVersion(v.nodeVersion)
+ return v;
+ });
+
+ // Sort by min version
+ const sortedMinVersions = minVersions.sort((v1, v2) => semver.compare(v1.nodeMinVersion, v2.nodeMinVersion));
+
+ // Filter by higher versions
+ const higherVersions = sortedMinVersions.filter(v => semver.gt(v.nodeMinVersion, baseVersion));
+ // console.log(`getHigherVersions: ${JSON.stringify(higherVersions)}`);
+ return higherVersions;
+ }
+
+ /**
+ * Returns the node version of the parent package.
+ * @return {Object} The parent package info.
+ */
+ async getParentVersion() {
+ // Get parent package.json version
+ const version = await this.getNodeVersion({ files: [ this.packageJsonPath ], clean: true });
+ // console.log(`getParentVersion: ${JSON.stringify(version)}`);
+ return version[0];
+ }
+}
+
+async function check() {
+ core = await import('@actions/core');
+ // Define paths
+ const nodeModulesPath = path.join(__dirname, '../node_modules');
+ const packageJsonPath = path.join(__dirname, '../package.json');
+
+ // Create check
+ const check = new NodeEngineCheck({
+ nodeModulesPath,
+ packageJsonPath,
+ });
+
+ // Get package node version of parent package
+ const parentVersion = await check.getParentVersion();
+
+ // If parent node version could not be determined
+ if (parentVersion === undefined) {
+ core.setFailed(`Failed to determine node engine version of parent package at ${this.packageJsonPath}`);
+ return;
+ }
+
+ // Determine parent min version
+ const parentMinVersion = semver.minVersion(parentVersion.nodeVersion);
+
+ // Get package.json files
+ const files = await check.getPackageFiles();
+ core.info(`Checking the minimum node version requirement of ${files.length} dependencies`);
+
+ // Get node versions
+ const versions = await check.getNodeVersion({ files, clean: true });
+
+ // Get are dependencies that require a higher node version than the parent package
+ const higherVersions = check.getHigherVersions({ versions, baseVersion: parentMinVersion });
+
+ // Get highest version
+ const highestVersion = higherVersions.map(v => v.nodeMinVersion).pop();
+
+ /* eslint-disable no-console */
+ // If there are higher versions
+ if (higherVersions.length > 0) {
+ console.log(`\nThere are ${higherVersions.length} dependencies that require a higher node engine version than the parent package (${parentVersion.nodeVersion}):`);
+
+ // For each dependency
+ for (const higherVersion of higherVersions) {
+
+ // Get package name
+ const _package = higherVersion.file.split('node_modules/').pop().replace('/package.json', '');
+ console.log(`- ${_package} requires at least node ${higherVersion.nodeMinVersion} (${higherVersion.nodeVersion})`);
+ }
+ console.log('');
+ core.setFailed(`â Upgrade the node engine version in package.json to at least '${highestVersion}' to satisfy the dependencies.`);
+ console.log('');
+ return;
+ }
+
+ console.log(`â
All dependencies satisfy the node version requirement of the parent package (${parentVersion.nodeVersion}).`);
+ /* eslint-enable no-console */
+}
+
+check();
diff --git a/ci/uninstallDevDeps.sh b/ci/uninstallDevDeps.sh
new file mode 100755
index 0000000000..633860223d
--- /dev/null
+++ b/ci/uninstallDevDeps.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Read package exclusion list from arguments
+exclusionList=("$@")
+
+# Convert exclusion list to grep pattern
+exclusionPattern=$(printf "|%s" "${exclusionList[@]}")
+exclusionPattern=${exclusionPattern:1}
+
+# Get list of all dev dependencies
+devDeps=$(jq -r '.devDependencies | keys | .[]' package.json)
+
+# Filter out exclusion list
+depsToUninstall=$(echo "$devDeps" | grep -Ev "$exclusionPattern")
+
+# If there are dependencies to uninstall then uninstall them
+if [ -n "$depsToUninstall" ]; then
+ echo "Uninstalling dev dependencies: $depsToUninstall"
+ npm uninstall $depsToUninstall
+else
+ echo "No dev dependencies to uninstall"
+fi
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000000..f9e0977938
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,86 @@
+const js = require("@eslint/js");
+const babelParser = require("@babel/eslint-parser");
+const globals = require("globals");
+const unusedImports = require("eslint-plugin-unused-imports");
+
+module.exports = [
+ {
+ ignores: ["**/lib/**", "**/coverage/**", "**/out/**", "**/types/**"],
+ },
+ js.configs.recommended,
+ {
+ languageOptions: {
+ parser: babelParser,
+ ecmaVersion: 6,
+ sourceType: "module",
+ globals: {
+ Parse: "readonly",
+ ...globals.node,
+ },
+ parserOptions: {
+ requireConfigFile: false,
+ },
+ },
+ plugins: {
+ "unused-imports": unusedImports,
+ },
+ rules: {
+ indent: ["error", 2, { SwitchCase: 1 }],
+ "unused-imports/no-unused-imports": "error",
+ "unused-imports/no-unused-vars": "error",
+ "linebreak-style": ["error", "unix"],
+ "no-trailing-spaces": "error",
+ "eol-last": "error",
+ "space-in-parens": ["error", "never"],
+ "no-multiple-empty-lines": "warn",
+ "prefer-const": "error",
+ "space-infix-ops": "error",
+ "no-useless-escape": "off",
+ "require-atomic-updates": "off",
+ "object-curly-spacing": ["error", "always"],
+ curly: ["error", "all"],
+ "block-spacing": ["error", "always"],
+ "no-unused-vars": "off",
+ "no-console": "warn",
+ "no-restricted-syntax": [
+ "error",
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Date']",
+ message: "Use Utils.isDate() instead of instanceof Date (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='RegExp']",
+ message: "Use Utils.isRegExp() instead of instanceof RegExp (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Error']",
+ message: "Use Utils.isNativeError() instead of instanceof Error (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Promise']",
+ message: "Use Utils.isPromise() instead of instanceof Promise (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Map']",
+ message: "Use Utils.isMap() instead of instanceof Map (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Object']",
+ message: "Use Utils.isObject() instead of instanceof Object (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Set']",
+ message: "Use Utils.isSet() instead of instanceof Set (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Buffer']",
+ message: "Use Buffer.isBuffer() instead of instanceof Buffer (cross-realm safe).",
+ },
+ {
+ selector: "BinaryExpression[operator='instanceof'][right.name='Array']",
+ message: "Use Array.isArray() instead of instanceof Array (cross-realm safe).",
+ },
+ ]
+ },
+ },
+];
diff --git a/jsdoc-conf.json b/jsdoc-conf.json
new file mode 100644
index 0000000000..e90f82556b
--- /dev/null
+++ b/jsdoc-conf.json
@@ -0,0 +1,40 @@
+{
+ "plugins": ["node_modules/jsdoc-babel", "plugins/markdown"],
+ "babel": {
+ "plugins": ["@babel/plugin-transform-flow-strip-types"]
+ },
+ "source": {
+ "include": [
+ "README.md",
+ "./lib/cloud-code",
+ "./lib/Options/docs.js",
+ "./lib/ParseServer.js",
+ "./lib/Adapters"
+ ],
+ "excludePattern": "(^|\\/|\\\\)_"
+ },
+ "templates": {
+ "default": {
+ "outputSourceFiles": false,
+ "showInheritedInNav": false,
+ "useLongnameInNav": true
+ },
+ "cleverLinks": true,
+ "monospaceLinks": false
+ },
+ "opts": {
+ "encoding": "utf8",
+ "readme": "./README.md",
+ "recurse": true,
+ "template": "./node_modules/clean-jsdoc-theme",
+ "theme_opts": {
+ "default_theme": "dark",
+ "title": "
",
+ "create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px; height: auto; max-width: 100%; object-fit: contain; }"
+ }
+ },
+ "markdown": {
+ "hardwrap": false,
+ "idInHeadings": true
+ }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000..4696594f09
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,45262 @@
+{
+ "name": "parse-server",
+ "version": "9.9.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "parse-server",
+ "version": "9.9.0",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@apollo/server": "5.5.0",
+ "@as-integrations/express5": "1.1.2",
+ "@fastify/busboy": "3.2.0",
+ "@graphql-tools/merge": "9.1.7",
+ "@graphql-tools/schema": "10.0.31",
+ "@graphql-tools/utils": "11.0.0",
+ "@parse/fs-files-adapter": "3.0.0",
+ "@parse/push-adapter": "8.4.0",
+ "bcryptjs": "3.0.3",
+ "commander": "14.0.3",
+ "cors": "2.8.6",
+ "express": "5.2.1",
+ "express-rate-limit": "8.3.1",
+ "follow-redirects": "1.15.11",
+ "graphql": "16.13.2",
+ "graphql-list-fields": "2.0.4",
+ "graphql-relay": "0.10.2",
+ "graphql-upload": "15.0.2",
+ "intersect": "1.0.1",
+ "jsonwebtoken": "9.0.3",
+ "jwks-rsa": "3.2.0",
+ "ldapjs": "3.0.7",
+ "lodash": "4.18.1",
+ "lru-cache": "11.2.7",
+ "mime": "4.1.0",
+ "mongodb": "7.1.0",
+ "mustache": "4.2.0",
+ "otpauth": "9.5.0",
+ "parse": "8.6.0",
+ "path-to-regexp": "8.4.2",
+ "pg-monitor": "3.1.0",
+ "pg-promise": "12.6.0",
+ "pluralize": "8.0.0",
+ "punycode": "2.3.1",
+ "rate-limit-redis": "4.3.1",
+ "redis": "5.11.0",
+ "semver": "7.7.4",
+ "tv4": "1.3.0",
+ "winston": "3.19.0",
+ "winston-daily-rotate-file": "5.0.0",
+ "ws": "8.20.0"
+ },
+ "bin": {
+ "parse-server": "bin/parse-server"
+ },
+ "devDependencies": {
+ "@actions/core": "3.0.0",
+ "@apollo/client": "3.13.8",
+ "@babel/cli": "7.28.6",
+ "@babel/core": "7.29.0",
+ "@babel/eslint-parser": "7.28.6",
+ "@babel/plugin-proposal-object-rest-spread": "7.20.7",
+ "@babel/plugin-transform-flow-strip-types": "7.27.1",
+ "@babel/preset-env": "7.29.2",
+ "@babel/preset-typescript": "7.27.1",
+ "@saithodev/semantic-release-backmerge": "4.0.1",
+ "@semantic-release/changelog": "6.0.3",
+ "@semantic-release/commit-analyzer": "13.0.1",
+ "@semantic-release/git": "10.0.1",
+ "@semantic-release/github": "12.0.6",
+ "@semantic-release/npm": "13.0.0",
+ "@semantic-release/release-notes-generator": "14.1.0",
+ "all-node-versions": "13.0.1",
+ "apollo-upload-client": "18.0.1",
+ "clean-jsdoc-theme": "4.3.0",
+ "cross-env": "7.0.3",
+ "deep-diff": "1.0.2",
+ "eslint": "9.27.0",
+ "eslint-plugin-expect-type": "0.6.2",
+ "eslint-plugin-unused-imports": "4.4.1",
+ "form-data": "4.0.5",
+ "globals": "17.3.0",
+ "graphql-tag": "2.12.6",
+ "jasmine": "6.1.0",
+ "jasmine-spec-reporter": "7.0.0",
+ "jsdoc": "4.0.5",
+ "jsdoc-babel": "0.5.0",
+ "lint-staged": "16.4.0",
+ "m": "1.10.0",
+ "madge": "8.0.0",
+ "mock-files-adapter": "file:spec/dependencies/mock-files-adapter",
+ "mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
+ "mongodb-runner": "5.9.3",
+ "node-abort-controller": "3.1.1",
+ "node-fetch": "3.3.2",
+ "nyc": "17.1.0",
+ "prettier": "3.8.1",
+ "semantic-release": "25.0.3",
+ "typescript": "5.9.3",
+ "typescript-eslint": "8.58.0",
+ "yaml": "2.8.3"
+ },
+ "engines": {
+ "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.11.0 <25.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parse-server"
+ },
+ "optionalDependencies": {
+ "@node-rs/bcrypt": "1.10.7"
+ }
+ },
+ "node_modules/@actions/core": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.0.tgz",
+ "integrity": "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==",
+ "dev": true,
+ "dependencies": {
+ "@actions/exec": "^3.0.0",
+ "@actions/http-client": "^4.0.0"
+ }
+ },
+ "node_modules/@actions/exec": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
+ "integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
+ "dev": true,
+ "dependencies": {
+ "@actions/io": "^3.0.2"
+ }
+ },
+ "node_modules/@actions/http-client": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
+ "integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
+ "dev": true,
+ "dependencies": {
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
+ }
+ },
+ "node_modules/@actions/io": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
+ "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
+ "dev": true
+ },
+ "node_modules/@apollo/cache-control-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz",
+ "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==",
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/client": {
+ "version": "3.13.8",
+ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz",
+ "integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@wry/caches": "^1.0.0",
+ "@wry/equality": "^0.5.6",
+ "@wry/trie": "^0.5.0",
+ "graphql-tag": "^2.12.6",
+ "hoist-non-react-statics": "^3.3.2",
+ "optimism": "^0.18.0",
+ "prop-types": "^15.7.2",
+ "rehackt": "^0.1.0",
+ "symbol-observable": "^4.0.0",
+ "ts-invariant": "^0.10.3",
+ "tslib": "^2.3.0",
+ "zen-observable-ts": "^1.2.5"
+ },
+ "peerDependencies": {
+ "graphql": "^15.0.0 || ^16.0.0",
+ "graphql-ws": "^5.5.5 || ^6.0.3",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+ "subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
+ },
+ "peerDependenciesMeta": {
+ "graphql-ws": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "subscriptions-transport-ws": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@apollo/protobufjs": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz",
+ "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.0",
+ "long": "^4.0.0"
+ },
+ "bin": {
+ "apollo-pbjs": "bin/pbjs",
+ "apollo-pbts": "bin/pbts"
+ }
+ },
+ "node_modules/@apollo/server": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz",
+ "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==",
+ "license": "MIT",
+ "dependencies": {
+ "@apollo/cache-control-types": "^1.0.3",
+ "@apollo/server-gateway-interface": "^2.0.0",
+ "@apollo/usage-reporting-protobuf": "^4.1.1",
+ "@apollo/utils.createhash": "^3.0.0",
+ "@apollo/utils.fetcher": "^3.0.0",
+ "@apollo/utils.isnodelike": "^3.0.0",
+ "@apollo/utils.keyvaluecache": "^4.0.0",
+ "@apollo/utils.logger": "^3.0.0",
+ "@apollo/utils.usagereporting": "^2.1.0",
+ "@apollo/utils.withrequired": "^3.0.0",
+ "@graphql-tools/schema": "^10.0.0",
+ "async-retry": "^1.2.1",
+ "body-parser": "^2.2.2",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "finalhandler": "^2.1.0",
+ "loglevel": "^1.6.8",
+ "lru-cache": "^11.1.0",
+ "negotiator": "^1.0.0",
+ "uuid": "^11.1.0",
+ "whatwg-mimetype": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "graphql": "^16.11.0"
+ }
+ },
+ "node_modules/@apollo/server-gateway-interface": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz",
+ "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==",
+ "license": "MIT",
+ "dependencies": {
+ "@apollo/usage-reporting-protobuf": "^4.1.1",
+ "@apollo/utils.fetcher": "^3.0.0",
+ "@apollo/utils.keyvaluecache": "^4.0.0",
+ "@apollo/utils.logger": "^3.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/server/node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/@apollo/usage-reporting-protobuf": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz",
+ "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==",
+ "dependencies": {
+ "@apollo/protobufjs": "1.2.7"
+ }
+ },
+ "node_modules/@apollo/utils.createhash": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz",
+ "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==",
+ "license": "MIT",
+ "dependencies": {
+ "@apollo/utils.isnodelike": "^3.0.0",
+ "sha.js": "^2.4.11"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@apollo/utils.dropunuseddefinitions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz",
+ "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==",
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.fetcher": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz",
+ "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@apollo/utils.isnodelike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz",
+ "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@apollo/utils.keyvaluecache": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz",
+ "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==",
+ "license": "MIT",
+ "dependencies": {
+ "@apollo/utils.logger": "^3.0.0",
+ "lru-cache": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@apollo/utils.logger": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz",
+ "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@apollo/utils.printwithreducedwhitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz",
+ "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==",
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.removealiases": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz",
+ "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==",
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.sortast": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz",
+ "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==",
+ "dependencies": {
+ "lodash.sortby": "^4.7.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.stripsensitiveliterals": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz",
+ "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==",
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.usagereporting": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz",
+ "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==",
+ "dependencies": {
+ "@apollo/usage-reporting-protobuf": "^4.1.0",
+ "@apollo/utils.dropunuseddefinitions": "^2.0.1",
+ "@apollo/utils.printwithreducedwhitespace": "^2.0.1",
+ "@apollo/utils.removealiases": "2.0.1",
+ "@apollo/utils.sortast": "^2.0.1",
+ "@apollo/utils.stripsensitiveliterals": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "graphql": "14.x || 15.x || 16.x"
+ }
+ },
+ "node_modules/@apollo/utils.withrequired": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz",
+ "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@as-integrations/express5": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@as-integrations/express5/-/express5-1.1.2.tgz",
+ "integrity": "sha512-BxfwtcWNf2CELDkuPQxi5Zl3WqY/dQVJYafeCBOGoFQjv5M0fjhxmAFZ9vKx/5YKKNeok4UY6PkFbHzmQrdxIA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "@apollo/server": "^4.0.0 || ^5.0.0",
+ "express": "^5.0.0"
+ }
+ },
+ "node_modules/@babel/cli": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz",
+ "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "commander": "^6.2.0",
+ "convert-source-map": "^2.0.0",
+ "fs-readdir-recursive": "^1.1.0",
+ "glob": "^7.2.0",
+ "make-dir": "^2.1.0",
+ "slash": "^2.0.0"
+ },
+ "bin": {
+ "babel": "bin/babel.js",
+ "babel-external-helpers": "bin/babel-external-helpers.js"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "optionalDependencies": {
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+ "chokidar": "^3.6.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/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
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/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
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/eslint-parser": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz",
+ "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==",
+ "dev": true,
+ "dependencies": {
+ "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+ "eslint-visitor-keys": "^2.1.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.11.0",
+ "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@babel/eslint-parser/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/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,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
+ "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/traverse": "^7.28.6",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "regexpu-core": "^6.3.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
+ "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "debug": "^4.4.3",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.11"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
+ "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz",
+ "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-wrap-function": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
+ "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
+ "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz",
+ "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
+ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
+ "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
+ "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
+ "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
+ "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+ "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-flow": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz",
+ "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
+ "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
+ "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
+ "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
+ "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
+ "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz",
+ "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-remap-async-to-generator": "^7.27.1",
+ "@babel/traverse": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz",
+ "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-remap-async-to-generator": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
+ "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz",
+ "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz",
+ "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
+ "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz",
+ "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-replace-supers": "^7.28.6",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz",
+ "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/template": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
+ "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
+ "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
+ "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz",
+ "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
+ "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
+ "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
+ "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
+ "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-flow-strip-types": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz",
+ "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-flow": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
+ "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
+ "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
+ "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
+ "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz",
+ "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
+ "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
+ "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz",
+ "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
+ "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
+ "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz",
+ "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
+ "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz",
+ "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz",
+ "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
+ "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
+ "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz",
+ "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz",
+ "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.27.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz",
+ "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz",
+ "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz",
+ "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
+ "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz",
+ "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
+ "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
+ "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
+ "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz",
+ "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz",
+ "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
+ "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
+ "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz",
+ "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-syntax-typescript": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
+ "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
+ "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
+ "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
+ "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz",
+ "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.28.6",
+ "@babel/plugin-syntax-import-attributes": "^7.28.6",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.27.1",
+ "@babel/plugin-transform-async-generator-functions": "^7.29.0",
+ "@babel/plugin-transform-async-to-generator": "^7.28.6",
+ "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
+ "@babel/plugin-transform-block-scoping": "^7.28.6",
+ "@babel/plugin-transform-class-properties": "^7.28.6",
+ "@babel/plugin-transform-class-static-block": "^7.28.6",
+ "@babel/plugin-transform-classes": "^7.28.6",
+ "@babel/plugin-transform-computed-properties": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-dotall-regex": "^7.28.6",
+ "@babel/plugin-transform-duplicate-keys": "^7.27.1",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-dynamic-import": "^7.27.1",
+ "@babel/plugin-transform-explicit-resource-management": "^7.28.6",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.6",
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
+ "@babel/plugin-transform-for-of": "^7.27.1",
+ "@babel/plugin-transform-function-name": "^7.27.1",
+ "@babel/plugin-transform-json-strings": "^7.28.6",
+ "@babel/plugin-transform-literals": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.6",
+ "@babel/plugin-transform-member-expression-literals": "^7.27.1",
+ "@babel/plugin-transform-modules-amd": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.28.6",
+ "@babel/plugin-transform-modules-systemjs": "^7.29.0",
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-new-target": "^7.27.1",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
+ "@babel/plugin-transform-numeric-separator": "^7.28.6",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.6",
+ "@babel/plugin-transform-object-super": "^7.27.1",
+ "@babel/plugin-transform-optional-catch-binding": "^7.28.6",
+ "@babel/plugin-transform-optional-chaining": "^7.28.6",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/plugin-transform-private-methods": "^7.28.6",
+ "@babel/plugin-transform-private-property-in-object": "^7.28.6",
+ "@babel/plugin-transform-property-literals": "^7.27.1",
+ "@babel/plugin-transform-regenerator": "^7.29.0",
+ "@babel/plugin-transform-regexp-modifiers": "^7.28.6",
+ "@babel/plugin-transform-reserved-words": "^7.27.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.27.1",
+ "@babel/plugin-transform-spread": "^7.28.6",
+ "@babel/plugin-transform-sticky-regex": "^7.27.1",
+ "@babel/plugin-transform-template-literals": "^7.27.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.27.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.27.1",
+ "@babel/plugin-transform-unicode-property-regex": "^7.28.6",
+ "@babel/plugin-transform-unicode-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.28.6",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.15",
+ "babel-plugin-polyfill-corejs3": "^0.14.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.6",
+ "core-js-compat": "^3.48.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/preset-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
+ "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-syntax-jsx": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.27.1",
+ "@babel/plugin-transform-typescript": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs3": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz",
+ "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==",
+ "license": "MIT",
+ "dependencies": {
+ "core-js-pure": "^3.48.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@dabh/diagnostics": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
+ "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
+ "dependencies": {
+ "@so-ric/colorspace": "^1.1.6",
+ "enabled": "2.0.x",
+ "kuler": "^2.0.0"
+ }
+ },
+ "node_modules/@dependents/detective-less": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.0.tgz",
+ "integrity": "sha512-D/9dozteKcutI5OdxJd8rU+fL6XgaaRg60sPPJWkT33OCiRfkCu5wO5B/yXTaaL2e6EB0lcCBGe5E0XscZCvvQ==",
+ "dev": true,
+ "dependencies": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
+ "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz",
+ "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "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.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
+ "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
+ "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
+ "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.15.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+ "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
+ "license": "MIT"
+ },
+ "node_modules/@firebase/app-check-interop-types": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz",
+ "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/app-types": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
+ "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/auth-interop-types": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz",
+ "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/component": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.2.tgz",
+ "integrity": "sha512-iyVDGc6Vjx7Rm0cAdccLH/NG6fADsgJak/XW9IA2lPf8AjIlsemOpFGKczYyPHxm4rnKdR8z6sK4+KEC7NwmEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/util": "1.15.0",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@firebase/database": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.2.tgz",
+ "integrity": "sha512-lP96CMjMPy/+d1d9qaaHjHHdzdwvEOuyyLq9ehX89e2XMKwS1jHNzYBO+42bdSumuj5ukPbmnFtViZu8YOMT+w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.3",
+ "@firebase/auth-interop-types": "0.2.4",
+ "@firebase/component": "0.7.2",
+ "@firebase/logger": "0.5.0",
+ "@firebase/util": "1.15.0",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@firebase/database-compat": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.2.tgz",
+ "integrity": "sha512-j4A6IhVZbgxAzT6gJJC2PfOxYCK9SrDrUO7nTM4EscTYtKkAkzsbKoCnDdjFapQfnsncvPWjqVTr/0PffUwg3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.7.2",
+ "@firebase/database": "1.1.2",
+ "@firebase/database-types": "1.0.18",
+ "@firebase/logger": "0.5.0",
+ "@firebase/util": "1.15.0",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@firebase/database-types": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.18.tgz",
+ "integrity": "sha512-yOY8IC2go9lfbVDMiy2ATun4EB2AFwocPaQADwMN/RHRUAZSM4rlAV7PGbWPSG/YhkJ2A9xQAiAENgSua9G5Fg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-types": "0.9.3",
+ "@firebase/util": "1.15.0"
+ }
+ },
+ "node_modules/@firebase/logger": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz",
+ "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@firebase/util": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.0.tgz",
+ "integrity": "sha512-AmWf3cHAOMbrCPG4xdPKQaj5iHnyYfyLKZxwz+Xf55bqKbpAmcYifB4jQinT2W9XhDRHISOoPyBOariJpCG6FA==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@google-cloud/firestore": {
+ "version": "7.11.6",
+ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz",
+ "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@opentelemetry/api": "^1.3.0",
+ "fast-deep-equal": "^3.1.1",
+ "functional-red-black-tree": "^1.0.1",
+ "google-gax": "^4.3.3",
+ "protobufjs": "^7.2.6"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/paginator": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
+ "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "arrify": "^2.0.0",
+ "extend": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/projectify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
+ "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/promisify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
+ "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz",
+ "integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@google-cloud/paginator": "^5.0.0",
+ "@google-cloud/projectify": "^4.0.0",
+ "@google-cloud/promisify": "<4.1.0",
+ "abort-controller": "^3.0.0",
+ "async-retry": "^1.3.3",
+ "duplexify": "^4.1.3",
+ "fast-xml-parser": "^5.3.4",
+ "gaxios": "^6.0.2",
+ "google-auth-library": "^9.6.3",
+ "html-entities": "^2.5.2",
+ "mime": "^3.0.0",
+ "p-limit": "^3.0.1",
+ "retry-request": "^7.0.0",
+ "teeny-request": "^9.0.0",
+ "uuid": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage/node_modules/gcp-metadata": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^6.1.1",
+ "google-logging-utils": "^0.0.2",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage/node_modules/google-auth-library": {
+ "version": "9.15.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^6.1.1",
+ "gcp-metadata": "^6.1.0",
+ "gtoken": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage/node_modules/google-logging-utils": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage/node_modules/mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@google-cloud/storage/node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@graphql-tools/merge": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz",
+ "integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/utils": "^11.0.0",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/schema": {
+ "version": "10.0.31",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz",
+ "integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/merge": "^9.1.7",
+ "@graphql-tools/utils": "^11.0.0",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/utils": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz",
+ "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@whatwg-node/promise-helpers": "^1.0.0",
+ "cross-inspect": "1.0.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-typed-document-node/core": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz",
+ "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==",
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+ }
+ },
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
+ "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@grpc/proto-loader": "^0.8.0",
+ "@js-sdsl/ordered-map": "^4.4.2"
+ },
+ "engines": {
+ "node": ">=12.10.0"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
+ "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.5.3",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/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==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@grpc/grpc-js/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/@grpc/grpc-js/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/proto-loader": {
+ "version": "0.7.15",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+ "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/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==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@grpc/proto-loader/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/@grpc/proto-loader/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@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,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "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",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "devOptional": true,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "devOptional": true
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "devOptional": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "devOptional": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "devOptional": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@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,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/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,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/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,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/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,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/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,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/load-nyc-config/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jasminejs/reporters": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@jasminejs/reporters/-/reporters-1.0.0.tgz",
+ "integrity": "sha512-rM3GG4vx2H1Gp5kYCTr9aKlOEJFd43pzpiMAiy5b1+FUc2ub4e6bS6yCi/WQNDzAa5MVp9++dwcoEtcIfoEnhA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@js-sdsl/ordered-map": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
+ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/@jsdoc/salty": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
+ "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">=v12.0.0"
+ }
+ },
+ "node_modules/@ldapjs/asn1": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz",
+ "integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA=="
+ },
+ "node_modules/@ldapjs/attribute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz",
+ "integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==",
+ "dependencies": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "node_modules/@ldapjs/change": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz",
+ "integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==",
+ "dependencies": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/attribute": "1.0.0"
+ }
+ },
+ "node_modules/@ldapjs/controls": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz",
+ "integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==",
+ "dependencies": {
+ "@ldapjs/asn1": "^1.2.0",
+ "@ldapjs/protocol": "^1.2.1"
+ }
+ },
+ "node_modules/@ldapjs/controls/node_modules/@ldapjs/asn1": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz",
+ "integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw=="
+ },
+ "node_modules/@ldapjs/dn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz",
+ "integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==",
+ "dependencies": {
+ "@ldapjs/asn1": "2.0.0",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "node_modules/@ldapjs/filter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz",
+ "integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==",
+ "dependencies": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "node_modules/@ldapjs/messages": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz",
+ "integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==",
+ "dependencies": {
+ "@ldapjs/asn1": "^2.0.0",
+ "@ldapjs/attribute": "^1.0.0",
+ "@ldapjs/change": "^1.0.0",
+ "@ldapjs/controls": "^2.1.0",
+ "@ldapjs/dn": "^1.1.0",
+ "@ldapjs/filter": "^2.1.1",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.2.0"
+ }
+ },
+ "node_modules/@ldapjs/protocol": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz",
+ "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ=="
+ },
+ "node_modules/@mongodb-js/mongodb-downloader": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.4.2.tgz",
+ "integrity": "sha512-uCd6nDtKuM2J12jgqPkApEvGQWfgZOq6yUitagvXYIqg6ofdqAnmMJO3e3wIph+Vi++dnLoMv0ME9geBzHYwDA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "decompress": "^4.2.1",
+ "mongodb-download-url": "^1.6.2",
+ "node-fetch": "^2.7.0",
+ "tar": "^6.1.15"
+ }
+ },
+ "node_modules/@mongodb-js/mongodb-downloader/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mongodb-js/mongodb-downloader/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@mongodb-js/mongodb-downloader/node_modules/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==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@mongodb-js/mongodb-downloader/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/@mongodb-js/saslprep": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
+ "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.5.tgz",
+ "integrity": "sha512-kwUxR7J9WLutBbulqg1dfOrMTwhMdXLdcGUhcbCcGwnPLt3gz19uHVdwH1syKVDbE022ZS2vZxOWflFLS0YTjw==",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.1.0",
+ "@emnapi/runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.9.0"
+ }
+ },
+ "node_modules/@nicolo-ribaudo/chokidar-2": {
+ "version": "2.1.8-no-fsevents.3",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
+ "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
+ "version": "5.1.1-v1",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
+ "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-scope": "5.1.1"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz",
+ "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@node-rs/bcrypt": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.10.7.tgz",
+ "integrity": "sha512-1wk0gHsUQC/ap0j6SJa2K34qNhomxXRcEe3T8cI5s+g6fgHBgLTN7U9LzWTG/HE6G4+2tWWLeCabk1wiYGEQSA==",
+ "optional": true,
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@node-rs/bcrypt-android-arm-eabi": "1.10.7",
+ "@node-rs/bcrypt-android-arm64": "1.10.7",
+ "@node-rs/bcrypt-darwin-arm64": "1.10.7",
+ "@node-rs/bcrypt-darwin-x64": "1.10.7",
+ "@node-rs/bcrypt-freebsd-x64": "1.10.7",
+ "@node-rs/bcrypt-linux-arm-gnueabihf": "1.10.7",
+ "@node-rs/bcrypt-linux-arm64-gnu": "1.10.7",
+ "@node-rs/bcrypt-linux-arm64-musl": "1.10.7",
+ "@node-rs/bcrypt-linux-x64-gnu": "1.10.7",
+ "@node-rs/bcrypt-linux-x64-musl": "1.10.7",
+ "@node-rs/bcrypt-wasm32-wasi": "1.10.7",
+ "@node-rs/bcrypt-win32-arm64-msvc": "1.10.7",
+ "@node-rs/bcrypt-win32-ia32-msvc": "1.10.7",
+ "@node-rs/bcrypt-win32-x64-msvc": "1.10.7"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-android-arm-eabi": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.10.7.tgz",
+ "integrity": "sha512-8dO6/PcbeMZXS3VXGEtct9pDYdShp2WBOWlDvSbcRwVqyB580aCBh0BEFmKYtXLzLvUK8Wf+CG3U6sCdILW1lA==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-android-arm64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.10.7.tgz",
+ "integrity": "sha512-UASFBS/CucEMHiCtL/2YYsAY01ZqVR1N7vSb94EOvG5iwW7BQO06kXXCTgj+Xbek9azxixrCUmo3WJnkJZ0hTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-darwin-arm64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.10.7.tgz",
+ "integrity": "sha512-DgzFdAt455KTuiJ/zYIyJcKFobjNDR/hnf9OS7pK5NRS13Nq4gLcSIIyzsgHwZHxsJWbLpHmFc1H23Y7IQoQBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-darwin-x64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.10.7.tgz",
+ "integrity": "sha512-SPWVfQ6sxSokoUWAKWD0EJauvPHqOGQTd7CxmYatcsUgJ/bruvEHxZ4bIwX1iDceC3FkOtmeHO0cPwR480n/xA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-freebsd-x64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.10.7.tgz",
+ "integrity": "sha512-gpa+Ixs6GwEx6U6ehBpsQetzUpuAGuAFbOiuLB2oo4N58yU4AZz1VIcWyWAHrSWRs92O0SHtmo2YPrMrwfBbSw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.10.7.tgz",
+ "integrity": "sha512-kYgJnTnpxrzl9sxYqzflobvMp90qoAlaX1oDL7nhNTj8OYJVDIk0jQgblj0bIkjmoPbBed53OJY/iu4uTS+wig==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-linux-arm64-gnu": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.10.7.tgz",
+ "integrity": "sha512-7cEkK2RA+gBCj2tCVEI1rDSJV40oLbSq7bQ+PNMHNI6jCoXGmj9Uzo7mg7ZRbNZ7piIyNH5zlJqutjo8hh/tmA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-linux-arm64-musl": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.10.7.tgz",
+ "integrity": "sha512-X7DRVjshhwxUqzdUKDlF55cwzh+wqWJ2E/tILvZPboO3xaNO07Um568Vf+8cmKcz+tiZCGP7CBmKbBqjvKN/Pw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-linux-x64-gnu": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.10.7.tgz",
+ "integrity": "sha512-LXRZsvG65NggPD12hn6YxVgH0W3VR5fsE/o1/o2D5X0nxKcNQGeLWnRzs5cP8KpoFOuk1ilctXQJn8/wq+Gn/Q==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-linux-x64-musl": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.10.7.tgz",
+ "integrity": "sha512-tCjHmct79OfcO3g5q21ME7CNzLzpw1MAsUXCLHLGWH+V6pp/xTvMbIcLwzkDj6TI3mxK6kehTn40SEjBkZ3Rog==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-wasm32-wasi": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.10.7.tgz",
+ "integrity": "sha512-4qXSihIKeVXYglfXZEq/QPtYtBUvR8d3S85k15Lilv3z5B6NSGQ9mYiNleZ7QHVLN2gEc5gmi7jM353DMH9GkA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-win32-arm64-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.10.7.tgz",
+ "integrity": "sha512-FdfUQrqmDfvC5jFhntMBkk8EI+fCJTx/I1v7Rj+Ezlr9rez1j1FmuUnywbBj2Cg15/0BDhwYdbyZ5GCMFli2aQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-win32-ia32-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.10.7.tgz",
+ "integrity": "sha512-lZLf4Cx+bShIhU071p5BZft4OvP4PGhyp542EEsb3zk34U5GLsGIyCjOafcF/2DGewZL6u8/aqoxbSuROkgFXg==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/bcrypt-win32-x64-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.10.7.tgz",
+ "integrity": "sha512-hdw7tGmN1DxVAMTzICLdaHpXjy+4rxaxnBMgI8seG1JL5e3VcRGsd1/1vVDogVp2cbsmgq+6d6yAY+D9lW/DCg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@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,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@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,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@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,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@octokit/auth-token": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
+ "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/core": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz",
+ "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/auth-token": "^6.0.0",
+ "@octokit/graphql": "^9.0.1",
+ "@octokit/request": "^10.0.2",
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "before-after-hook": "^4.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/core/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/endpoint": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz",
+ "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^14.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz",
+ "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/request": "^10.0.2",
+ "@octokit/types": "^14.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/graphql/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
+ "dev": true
+ },
+ "node_modules/@octokit/plugin-paginate-rest": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
+ "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^16.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=6"
+ }
+ },
+ "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
+ "version": "27.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
+ "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
+ "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^27.0.0"
+ }
+ },
+ "node_modules/@octokit/plugin-retry": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz",
+ "integrity": "sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=7"
+ }
+ },
+ "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/plugin-throttling": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz",
+ "integrity": "sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^14.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "@octokit/core": "^7.0.0"
+ }
+ },
+ "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/request": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz",
+ "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/endpoint": "^11.0.0",
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "fast-content-type-parse": "^3.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/request-error": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz",
+ "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^14.0.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/request-error/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@octokit/request/node_modules/@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ },
+ "node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^22.2.0"
+ }
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@parse/fs-files-adapter": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
+ "integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
+ },
+ "node_modules/@parse/node-apn": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-8.0.0.tgz",
+ "integrity": "sha512-blvU/V0FL3j7u2lstso1aInMw7yYrKg/6Ctr3Kc/7kleFatAfZswhzHk9d5lI4DUQBsUBun8nidgZHCY6sft+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4.4.3",
+ "jsonwebtoken": "9.0.3",
+ "node-forge": "1.4.0",
+ "verror": "1.10.1"
+ },
+ "engines": {
+ "node": "20 || 22 || 24"
+ }
+ },
+ "node_modules/@parse/push-adapter": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.4.0.tgz",
+ "integrity": "sha512-sWinUJZvbWIH6cJfIRuwUCcsjvi6IkoJ3zp2JoCP/mLzItt6NPNk+j73RE9UJzIKlwt3NciWXeSHoxprPnNH/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@parse/node-apn": "8.0.0",
+ "expo-server-sdk": "6.1.0",
+ "firebase-admin": "13.7.0",
+ "npmlog": "7.0.1",
+ "parse": "8.5.0",
+ "web-push": "3.6.7"
+ },
+ "engines": {
+ "node": "20 || 22 || 24"
+ }
+ },
+ "node_modules/@parse/push-adapter/node_modules/@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@parse/push-adapter/node_modules/@babel/runtime-corejs3": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz",
+ "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==",
+ "license": "MIT",
+ "dependencies": {
+ "core-js-pure": "^3.48.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@parse/push-adapter/node_modules/parse": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.5.0.tgz",
+ "integrity": "sha512-X9gI4Yjbi9LPMPnCtKL4h0Nxe1aSCFMPWcB1zbu11qU/Be3eVSB5I5IMBunTuWlVz6Wchu3dtM5jl/1aBZ9wiQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "7.28.6",
+ "@babel/runtime-corejs3": "7.29.0",
+ "crypto-js": "4.2.0",
+ "idb-keyval": "6.2.2",
+ "react-native-crypto-js": "1.0.0",
+ "ws": "8.19.0"
+ },
+ "engines": {
+ "node": ">=20.19.0 <21 || >=22.12.0 <23 || >=24.1.0 <25"
+ }
+ },
+ "node_modules/@parse/push-adapter/node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@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,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "4.2.10"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/npm-conf": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
+ "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
+ "dev": true,
+ "dependencies": {
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "node_modules/@redis/bloom": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.11.0.tgz",
+ "integrity": "sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.11.0"
+ }
+ },
+ "node_modules/@redis/client": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.11.0.tgz",
+ "integrity": "sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cluster-key-slot": "1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@node-rs/xxhash": "^1.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@node-rs/xxhash": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@redis/json": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.11.0.tgz",
+ "integrity": "sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.11.0"
+ }
+ },
+ "node_modules/@redis/search": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.11.0.tgz",
+ "integrity": "sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.11.0"
+ }
+ },
+ "node_modules/@redis/time-series": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.11.0.tgz",
+ "integrity": "sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.11.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@saithodev/semantic-release-backmerge/-/semantic-release-backmerge-4.0.1.tgz",
+ "integrity": "sha512-WDsU28YrXSLx0xny7FgFlEk8DCKGcj6OOhA+4Q9k3te1jJD1GZuqY8sbIkVQaw9cqJ7CT+fCZUN6QDad8JW4Dg==",
+ "dev": true,
+ "dependencies": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.1.0",
+ "debug": "^4.3.4",
+ "execa": "^5.1.1",
+ "lodash": "^4.17.21",
+ "semantic-release": "^22.0.7"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/auth-token": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/core": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
+ "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/auth-token": "^4.0.0",
+ "@octokit/graphql": "^7.1.0",
+ "@octokit/request": "^8.3.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/endpoint": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
+ "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/graphql": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
+ "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/request": "^8.3.0",
+ "@octokit/types": "^13.0.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/openapi-types": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
+ "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
+ "dev": true
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-paginate-rest": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
+ "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/types": "^12.6.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": "5"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-retry": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
+ "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/request-error": "^5.0.0",
+ "@octokit/types": "^12.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=5"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-throttling": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz",
+ "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/types": "^12.2.0",
+ "bottleneck": "^2.15.3"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": "^5.0.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/request": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
+ "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/endpoint": "^9.0.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@octokit/request-error": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
+ "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/types": "^13.1.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/commit-analyzer": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-11.1.0.tgz",
+ "integrity": "sha512-cXNTbv3nXR2hlzHjAMgbuiQVtvWHTlwwISt60B+4NZv01y/QRY7p2HcJm8Eh2StzcTJoNnflvKjHH/cjFS7d5g==",
+ "dev": true,
+ "dependencies": {
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-commits-filter": "^4.0.0",
+ "conventional-commits-parser": "^5.0.0",
+ "debug": "^4.0.0",
+ "import-from-esm": "^1.0.3",
+ "lodash-es": "^4.17.21",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/github": {
+ "version": "9.2.6",
+ "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.6.tgz",
+ "integrity": "sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==",
+ "dev": true,
+ "dependencies": {
+ "@octokit/core": "^5.0.0",
+ "@octokit/plugin-paginate-rest": "^9.0.0",
+ "@octokit/plugin-retry": "^6.0.0",
+ "@octokit/plugin-throttling": "^8.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "debug": "^4.3.4",
+ "dir-glob": "^3.0.1",
+ "globby": "^14.0.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "issue-parser": "^6.0.0",
+ "lodash-es": "^4.17.21",
+ "mime": "^4.0.0",
+ "p-filter": "^4.0.0",
+ "url-join": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/github/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/github/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/npm": {
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-11.0.3.tgz",
+ "integrity": "sha512-KUsozQGhRBAnoVg4UMZj9ep436VEGwT536/jwSqB7vcEfA6oncCUU7UIYTRdLx7GvTtqn0kBjnkfLVkcnBa2YQ==",
+ "dev": true,
+ "dependencies": {
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "execa": "^8.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^8.0.0",
+ "npm": "^10.5.0",
+ "rc": "^1.2.8",
+ "read-pkg": "^9.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ },
+ "engines": {
+ "node": "^18.17 || >=20"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/npm/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/npm/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/npm/node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/npm/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/release-notes-generator": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-12.1.0.tgz",
+ "integrity": "sha512-g6M9AjUKAZUZnxaJZnouNBeDNTCUrJ5Ltj+VJ60gJeDaRRahcHsry9HW8yKrnKkKNkx5lbWiEP1FPMqVNQz8Kg==",
+ "dev": true,
+ "dependencies": {
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-changelog-writer": "^7.0.0",
+ "conventional-commits-filter": "^4.0.0",
+ "conventional-commits-parser": "^5.0.0",
+ "debug": "^4.0.0",
+ "get-stream": "^7.0.0",
+ "import-from-esm": "^1.0.3",
+ "into-stream": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "read-pkg-up": "^11.0.0"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz",
+ "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/ansi-escapes": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
+ "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/before-after-hook": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
+ "dev": true
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "5.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/conventional-changelog-angular": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+ "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+ "dev": true,
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/conventional-changelog-writer": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.1.tgz",
+ "integrity": "sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==",
+ "dev": true,
+ "dependencies": {
+ "conventional-commits-filter": "^4.0.0",
+ "handlebars": "^4.7.7",
+ "json-stringify-safe": "^5.0.1",
+ "meow": "^12.0.1",
+ "semver": "^7.5.2",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "conventional-changelog-writer": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/conventional-commits-filter": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz",
+ "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/conventional-commits-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+ "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+ "dev": true,
+ "dependencies": {
+ "is-text-path": "^2.0.0",
+ "JSONStream": "^1.3.5",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "dependencies": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/env-ci": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-10.0.0.tgz",
+ "integrity": "sha512-U4xcd/utDYFgMh0yWj07R1H6L5fwhVbmxBCpnL0DbVSDZVnsC82HONw0wxtxNkIAcua3KtbomQvIk5xFZGAQJw==",
+ "dev": true,
+ "dependencies": {
+ "execa": "^8.0.0",
+ "java-properties": "^1.0.2"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/env-ci/node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/env-ci/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/find-versions": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz",
+ "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==",
+ "dev": true,
+ "dependencies": {
+ "semver-regex": "^4.0.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/globby": {
+ "version": "14.0.2",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz",
+ "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==",
+ "dev": true,
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^2.1.0",
+ "fast-glob": "^3.3.2",
+ "ignore": "^5.2.4",
+ "path-type": "^5.0.0",
+ "slash": "^5.1.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/globby/node_modules/path-type": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
+ "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/issue-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz",
+ "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==",
+ "dev": true,
+ "dependencies": {
+ "lodash.capitalize": "^4.2.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.uniqby": "^4.7.0"
+ },
+ "engines": {
+ "node": ">=10.13"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/marked": {
+ "version": "9.1.6",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
+ "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==",
+ "dev": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/marked-terminal": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
+ "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^6.2.0",
+ "cardinal": "^2.1.1",
+ "chalk": "^5.3.0",
+ "cli-table3": "^0.6.3",
+ "node-emoji": "^2.1.3",
+ "supports-hyperlinks": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "marked": ">=1 <12"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/meow": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+ "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/p-reduce": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
+ "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/semantic-release": {
+ "version": "22.0.12",
+ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.12.tgz",
+ "integrity": "sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==",
+ "dev": true,
+ "dependencies": {
+ "@semantic-release/commit-analyzer": "^11.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "@semantic-release/github": "^9.0.0",
+ "@semantic-release/npm": "^11.0.0",
+ "@semantic-release/release-notes-generator": "^12.0.0",
+ "aggregate-error": "^5.0.0",
+ "cosmiconfig": "^8.0.0",
+ "debug": "^4.0.0",
+ "env-ci": "^10.0.0",
+ "execa": "^8.0.0",
+ "figures": "^6.0.0",
+ "find-versions": "^5.1.0",
+ "get-stream": "^6.0.0",
+ "git-log-parser": "^1.2.0",
+ "hook-std": "^3.0.0",
+ "hosted-git-info": "^7.0.0",
+ "import-from-esm": "^1.3.1",
+ "lodash-es": "^4.17.21",
+ "marked": "^9.0.0",
+ "marked-terminal": "^6.0.0",
+ "micromatch": "^4.0.2",
+ "p-each-series": "^3.0.0",
+ "p-reduce": "^3.0.0",
+ "read-pkg-up": "^11.0.0",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.3.2",
+ "semver-diff": "^4.0.0",
+ "signale": "^1.2.1",
+ "yargs": "^17.5.1"
+ },
+ "bin": {
+ "semantic-release": "bin/semantic-release.js"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/semantic-release/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/semantic-release/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/semantic-release/node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/semantic-release/node_modules/execa/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/slash": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
+ "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/universal-user-agent": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+ "dev": true
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/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,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@saithodev/semantic-release-backmerge/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@sec-ant/readable-stream": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+ "dev": true
+ },
+ "node_modules/@semantic-release/changelog": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz",
+ "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==",
+ "dev": true,
+ "dependencies": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash": "^4.17.4"
+ },
+ "engines": {
+ "node": ">=14.17"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/commit-analyzer": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz",
+ "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==",
+ "dev": true,
+ "dependencies": {
+ "conventional-changelog-angular": "^8.0.0",
+ "conventional-changelog-writer": "^8.0.0",
+ "conventional-commits-filter": "^5.0.0",
+ "conventional-commits-parser": "^6.0.0",
+ "debug": "^4.0.0",
+ "import-from-esm": "^2.0.0",
+ "lodash-es": "^4.17.21",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=20.8.1"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@semantic-release/commit-analyzer/node_modules/import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.20"
+ }
+ },
+ "node_modules/@semantic-release/error": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz",
+ "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/@semantic-release/git": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz",
+ "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==",
+ "dev": true,
+ "dependencies": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.0.0",
+ "debug": "^4.0.0",
+ "dir-glob": "^3.0.0",
+ "execa": "^5.0.0",
+ "lodash": "^4.17.4",
+ "micromatch": "^4.0.0",
+ "p-reduce": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.17"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/github": {
+ "version": "12.0.6",
+ "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-12.0.6.tgz",
+ "integrity": "sha512-aYYFkwHW3c6YtHwQF0t0+lAjlU+87NFOZuH2CvWFD0Ylivc7MwhZMiHOJ0FMpIgPpCVib/VUAcOwvrW0KnxQtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/core": "^7.0.0",
+ "@octokit/plugin-paginate-rest": "^14.0.0",
+ "@octokit/plugin-retry": "^8.0.0",
+ "@octokit/plugin-throttling": "^11.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "debug": "^4.3.4",
+ "dir-glob": "^3.0.1",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "issue-parser": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "mime": "^4.0.0",
+ "p-filter": "^4.0.0",
+ "tinyglobby": "^0.2.14",
+ "undici": "^7.0.0",
+ "url-join": "^5.0.0"
+ },
+ "engines": {
+ "node": "^22.14.0 || >= 24.10.0"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=24.1.0"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "5.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/github/node_modules/undici": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz",
+ "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/@semantic-release/npm": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-13.0.0.tgz",
+ "integrity": "sha512-7RIx9nUdUekYbIZ0dG7k7G/iSvUCZb03LmmBPFqAQEhPVC+BnHfhFxj5ewSNP6zMUsYaEQSckcOhKD8AuS/EzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "execa": "^9.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^8.0.0",
+ "npm": "^11.6.2",
+ "rc": "^1.2.8",
+ "read-pkg": "^9.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ },
+ "engines": {
+ "node": "^22.14.0 || >= 24.10.0"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "5.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/execa": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
+ "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
+ "dev": true,
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "cross-spawn": "^7.0.3",
+ "figures": "^6.1.0",
+ "get-stream": "^9.0.0",
+ "human-signals": "^7.0.0",
+ "is-plain-obj": "^4.1.0",
+ "is-stream": "^4.0.1",
+ "npm-run-path": "^5.2.0",
+ "pretty-ms": "^9.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^4.0.0",
+ "yoctocolors": "^2.0.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.5.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "dev": true,
+ "dependencies": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/human-signals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
+ "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-11.12.0.tgz",
+ "integrity": "sha512-xPhOap4ZbJWyd7DAOukP564WFwNSGu/2FeTRFHhiiKthcauxhH/NpkJAQm24xD+cAn8av5tQ00phi98DqtfLsg==",
+ "bundleDependencies": [
+ "@isaacs/string-locale-compare",
+ "@npmcli/arborist",
+ "@npmcli/config",
+ "@npmcli/fs",
+ "@npmcli/map-workspaces",
+ "@npmcli/metavuln-calculator",
+ "@npmcli/package-json",
+ "@npmcli/promise-spawn",
+ "@npmcli/redact",
+ "@npmcli/run-script",
+ "@sigstore/tuf",
+ "abbrev",
+ "archy",
+ "cacache",
+ "chalk",
+ "ci-info",
+ "fastest-levenshtein",
+ "fs-minipass",
+ "glob",
+ "graceful-fs",
+ "hosted-git-info",
+ "ini",
+ "init-package-json",
+ "is-cidr",
+ "json-parse-even-better-errors",
+ "libnpmaccess",
+ "libnpmdiff",
+ "libnpmexec",
+ "libnpmfund",
+ "libnpmorg",
+ "libnpmpack",
+ "libnpmpublish",
+ "libnpmsearch",
+ "libnpmteam",
+ "libnpmversion",
+ "make-fetch-happen",
+ "minimatch",
+ "minipass",
+ "minipass-pipeline",
+ "ms",
+ "node-gyp",
+ "nopt",
+ "npm-audit-report",
+ "npm-install-checks",
+ "npm-package-arg",
+ "npm-pick-manifest",
+ "npm-profile",
+ "npm-registry-fetch",
+ "npm-user-validate",
+ "p-map",
+ "pacote",
+ "parse-conflict-json",
+ "proc-log",
+ "qrcode-terminal",
+ "read",
+ "semver",
+ "spdx-expression-parse",
+ "ssri",
+ "supports-color",
+ "tar",
+ "text-table",
+ "tiny-relative-date",
+ "treeverse",
+ "validate-npm-package-name",
+ "which"
+ ],
+ "dev": true,
+ "license": "Artistic-2.0",
+ "workspaces": [
+ "docs",
+ "smoke-tests",
+ "mock-globals",
+ "mock-registry",
+ "workspaces/*"
+ ],
+ "dependencies": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/config": "^10.8.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/map-workspaces": "^5.0.3",
+ "@npmcli/metavuln-calculator": "^9.0.3",
+ "@npmcli/package-json": "^7.0.5",
+ "@npmcli/promise-spawn": "^9.0.1",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.4",
+ "@sigstore/tuf": "^4.0.2",
+ "abbrev": "^4.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^20.0.4",
+ "chalk": "^5.6.2",
+ "ci-info": "^4.4.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^13.0.6",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^9.0.2",
+ "ini": "^6.0.0",
+ "init-package-json": "^8.2.5",
+ "is-cidr": "^6.0.3",
+ "json-parse-even-better-errors": "^5.0.0",
+ "libnpmaccess": "^10.0.3",
+ "libnpmdiff": "^8.1.5",
+ "libnpmexec": "^10.2.5",
+ "libnpmfund": "^7.0.19",
+ "libnpmorg": "^8.0.1",
+ "libnpmpack": "^9.1.5",
+ "libnpmpublish": "^11.1.3",
+ "libnpmsearch": "^9.0.1",
+ "libnpmteam": "^8.0.2",
+ "libnpmversion": "^8.0.3",
+ "make-fetch-happen": "^15.0.5",
+ "minimatch": "^10.2.4",
+ "minipass": "^7.1.3",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^12.2.0",
+ "nopt": "^9.0.0",
+ "npm-audit-report": "^7.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.2",
+ "npm-pick-manifest": "^11.0.3",
+ "npm-profile": "^12.0.1",
+ "npm-registry-fetch": "^19.1.1",
+ "npm-user-validate": "^4.0.0",
+ "p-map": "^7.0.4",
+ "pacote": "^21.5.0",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.1.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^5.0.1",
+ "semver": "^7.7.4",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^13.0.1",
+ "supports-color": "^10.2.2",
+ "tar": "^7.5.11",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^2.0.2",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^7.0.2",
+ "which": "^6.0.1"
+ },
+ "bin": {
+ "npm": "bin/npm-cli.js",
+ "npx": "bin/npx-cli.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@gar/promise-retry": {
+ "version": "1.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/agent": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^11.2.1",
+ "socks-proxy-agent": "^8.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/arborist": {
+ "version": "9.4.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/metavuln-calculator": "^9.0.2",
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/query": "^5.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "bin-links": "^6.0.0",
+ "cacache": "^20.0.1",
+ "common-ancestor-path": "^2.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^11.2.1",
+ "minimatch": "^10.0.3",
+ "nopt": "^9.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "pacote": "^21.0.2",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.0.0",
+ "proggy": "^4.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "semver": "^7.3.7",
+ "ssri": "^13.0.0",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^4.0.0"
+ },
+ "bin": {
+ "arborist": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/config": {
+ "version": "10.8.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "ini": "^6.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "walk-up-path": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/fs": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/git": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "ini": "^6.0.0",
+ "lru-cache": "^11.2.1",
+ "npm-pick-manifest": "^11.0.1",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "which": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/installed-package-contents": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-bundled": "^5.0.0",
+ "npm-normalize-package-bin": "^5.0.0"
+ },
+ "bin": {
+ "installed-package-contents": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/map-workspaces": {
+ "version": "5.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "glob": "^13.0.0",
+ "minimatch": "^10.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
+ "version": "9.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cacache": "^20.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "pacote": "^21.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/name-from-folder": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/node-gyp": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/package-json": {
+ "version": "7.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^7.0.0",
+ "glob": "^13.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.5.3",
+ "spdx-expression-parse": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/promise-spawn": {
+ "version": "9.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "which": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/query": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/redact": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@npmcli/run-script": {
+ "version": "10.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "node-gyp": "^12.1.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/bundle": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.5.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/core": {
+ "version": "3.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/protobuf-specs": {
+ "version": "0.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/sign": {
+ "version": "4.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.2",
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.2.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "make-fetch-happen": "^15.0.4",
+ "proc-log": "^6.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/tuf": {
+ "version": "4.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "tuf-js": "^4.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@sigstore/verify": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/@tufjs/models": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^10.1.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/abbrev": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/agent-base": {
+ "version": "7.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/aproba": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/archy": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/bin-links": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cmd-shim": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "read-cmd-shim": "^6.0.0",
+ "write-file-atomic": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/binary-extensions": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cacache": {
+ "version": "20.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^5.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^13.0.0",
+ "lru-cache": "^11.1.0",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^7.0.2",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chalk": {
+ "version": "5.6.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/chownr": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ci-info": {
+ "version": "4.4.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cidr-regex": {
+ "version": "5.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cmd-shim": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/common-ancestor-path": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/debug": {
+ "version": "4.4.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/diff": {
+ "version": "8.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/env-paths": {
+ "version": "2.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/exponential-backoff": {
+ "version": "3.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/fs-minipass": {
+ "version": "3.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/glob": {
+ "version": "13.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/hosted-git-info": {
+ "version": "9.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^11.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ignore-walk": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minimatch": "^10.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ini": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/init-package-json": {
+ "version": "8.2.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/package-json": "^7.0.0",
+ "npm-package-arg": "^13.0.0",
+ "promzard": "^3.0.1",
+ "read": "^5.0.1",
+ "semver": "^7.7.2",
+ "validate-npm-package-name": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ip-address": {
+ "version": "10.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/is-cidr": {
+ "version": "6.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "cidr-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/isexe": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-parse-even-better-errors": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/json-stringify-nice": {
+ "version": "1.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/jsonparse": {
+ "version": "1.3.1",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff": {
+ "version": "6.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/just-diff-apply": {
+ "version": "5.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmaccess": {
+ "version": "10.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmdiff": {
+ "version": "8.1.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "binary-extensions": "^3.0.0",
+ "diff": "^8.0.2",
+ "minimatch": "^10.0.3",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "tar": "^7.5.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmexec": {
+ "version": "10.2.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "proc-log": "^6.0.0",
+ "read": "^5.0.1",
+ "semver": "^7.3.7",
+ "signal-exit": "^4.1.0",
+ "walk-up-path": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmfund": {
+ "version": "7.0.19",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmorg": {
+ "version": "8.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpack": {
+ "version": "9.1.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/run-script": "^10.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmpublish": {
+ "version": "11.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmsearch": {
+ "version": "9.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmteam": {
+ "version": "8.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/libnpmversion": {
+ "version": "8.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/lru-cache": {
+ "version": "11.2.7",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/make-fetch-happen": {
+ "version": "15.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/agent": "^4.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "cacache": "^20.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^1.0.0",
+ "proc-log": "^6.0.0",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minimatch": {
+ "version": "10.2.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass": {
+ "version": "7.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-collect": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-fetch": {
+ "version": "5.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.0.3",
+ "minipass-sized": "^2.0.0",
+ "minizlib": "^3.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ },
+ "optionalDependencies": {
+ "iconv-lite": "^0.7.2"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-flush/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-flush/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-pipeline/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minipass-sized": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/minizlib": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/mute-stream": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/negotiator": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/node-gyp": {
+ "version": "12.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^15.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.5.4",
+ "tinyglobby": "^0.2.12",
+ "which": "^6.0.0"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/nopt": {
+ "version": "9.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^4.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-audit-report": {
+ "version": "7.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-bundled": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-normalize-package-bin": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-install-checks": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-normalize-package-bin": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-package-arg": {
+ "version": "13.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-packlist": {
+ "version": "10.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "ignore-walk": "^8.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-pick-manifest": {
+ "version": "11.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-install-checks": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "npm-package-arg": "^13.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-profile": {
+ "version": "12.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-registry-fetch": {
+ "version": "19.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/redact": "^4.0.0",
+ "jsonparse": "^1.3.1",
+ "make-fetch-happen": "^15.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minizlib": "^3.0.1",
+ "npm-package-arg": "^13.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/npm-user-validate": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/p-map": {
+ "version": "7.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/pacote": {
+ "version": "21.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "cacache": "^20.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^13.0.0",
+ "npm-packlist": "^10.0.1",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0",
+ "tar": "^7.4.3"
+ },
+ "bin": {
+ "pacote": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/parse-conflict-json": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^5.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/path-scurry": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/postcss-selector-parser": {
+ "version": "7.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/proc-log": {
+ "version": "6.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/proggy": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-all-reject-late": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promise-call-limit": {
+ "version": "3.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/promzard": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "read": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/qrcode-terminal": {
+ "version": "0.12.0",
+ "dev": true,
+ "inBundle": true,
+ "bin": {
+ "qrcode-terminal": "bin/qrcode-terminal.js"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "mute-stream": "^3.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/read-cmd-shim": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/semver": {
+ "version": "7.7.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/sigstore": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "@sigstore/sign": "^4.1.0",
+ "@sigstore/tuf": "^4.0.1",
+ "@sigstore/verify": "^3.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks": {
+ "version": "2.8.7",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-exceptions": {
+ "version": "2.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-expression-parse": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/spdx-license-ids": {
+ "version": "3.0.23",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/ssri": {
+ "version": "13.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/supports-color": {
+ "version": "10.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tar": {
+ "version": "7.5.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tiny-relative-date": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/treeverse": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/tuf-js": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/models": "4.1.0",
+ "debug": "^4.4.3",
+ "make-fetch-happen": "^15.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/validate-npm-package-name": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/walk-up-path": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/which": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^4.0.0"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/write-file-atomic": {
+ "version": "7.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/npm/node_modules/yallist": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/pretty-ms": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
+ "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
+ "dev": true,
+ "dependencies": {
+ "parse-ms": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@semantic-release/npm/node_modules/strip-final-newline": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+ "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/release-notes-generator": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.1.0.tgz",
+ "integrity": "sha512-CcyDRk7xq+ON/20YNR+1I/jP7BYKICr1uKd1HHpROSnnTdGqOTburi4jcRiTYz0cpfhxSloQO3cGhnoot7IEkA==",
+ "dev": true,
+ "dependencies": {
+ "conventional-changelog-angular": "^8.0.0",
+ "conventional-changelog-writer": "^8.0.0",
+ "conventional-commits-filter": "^5.0.0",
+ "conventional-commits-parser": "^6.0.0",
+ "debug": "^4.0.0",
+ "get-stream": "^7.0.0",
+ "import-from-esm": "^2.0.0",
+ "into-stream": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "read-package-up": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=20.8.1"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz",
+ "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@semantic-release/release-notes-generator/node_modules/import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.20"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@sindresorhus/merge-streams": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
+ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@so-ric/colorspace": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
+ "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
+ "dependencies": {
+ "color": "^5.0.2",
+ "text-hex": "1.0.x"
+ }
+ },
+ "node_modules/@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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@ts-graphviz/adapter": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.5.tgz",
+ "integrity": "sha512-K/xd2SJskbSLcUz9uYW9IDy26I3Oyutj/LREjJgcuLMxT3um4sZfy9LiUhGErHjxLRaNcaDVGSsmWeiNuhidXg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ts-graphviz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ts-graphviz"
+ }
+ ],
+ "dependencies": {
+ "@ts-graphviz/common": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ts-graphviz/ast": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/ast/-/ast-2.0.5.tgz",
+ "integrity": "sha512-HVT+Bn/smDzmKNJFccwgrpJaEUMPzXQ8d84JcNugzTHNUVgxAIe2Vbf4ug351YJpowivQp6/N7XCluQMjtgi5w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ts-graphviz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ts-graphviz"
+ }
+ ],
+ "dependencies": {
+ "@ts-graphviz/common": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ts-graphviz/common": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/common/-/common-2.1.4.tgz",
+ "integrity": "sha512-PNEzOgE4vgvorp/a4Ev26jVNtiX200yODoyPa8r6GfpPZbxWKW6bdXF6xWqzMkQoO1CnJOYJx2VANDbGqCqCCw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ts-graphviz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ts-graphviz"
+ }
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ts-graphviz/core": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/core/-/core-2.0.5.tgz",
+ "integrity": "sha512-YwaCGAG3Hs0nhxl+2lVuwuTTAK3GO2XHqOGvGIwXQB16nV858rrR5w2YmWCw9nhd11uLTStxLsCAhI9koWBqDA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ts-graphviz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ts-graphviz"
+ }
+ ],
+ "dependencies": {
+ "@ts-graphviz/ast": "^2.0.5",
+ "@ts-graphviz/common": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.5",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
+ "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/busboy": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.3.tgz",
+ "integrity": "sha512-YMBLFN/xBD8bnqywIlGyYqsNFXu6bsiY7h3Ae0kO17qEuTjsqeyYMRPSUDacIKIquws2Y6KjmxAyNx8xB3xQbw==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/caseless": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
+ "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.43",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz",
+ "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
+ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz",
+ "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true
+ },
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==",
+ "dev": true,
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
+ },
+ "node_modules/@types/node": {
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
+ "dependencies": {
+ "undici-types": "~6.19.8"
+ }
+ },
+ "node_modules/@types/normalize-package-data": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+ "dev": true
+ },
+ "node_modules/@types/object-path": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.4.tgz",
+ "integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw=="
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.11",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
+ "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ=="
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
+ },
+ "node_modules/@types/request": {
+ "version": "2.48.13",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
+ "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/caseless": "*",
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "form-data": "^2.5.5"
+ }
+ },
+ "node_modules/@types/request/node_modules/form-data": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
+ "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.35",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+ "dev": true
+ },
+ "node_modules/@types/send": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
+ "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
+ "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
+ },
+ "node_modules/@types/webidl-conversions": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
+ },
+ "node_modules/@types/whatwg-url": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
+ "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
+ "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/type-utils": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.58.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
+ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
+ "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.58.0",
+ "@typescript-eslint/types": "^8.58.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
+ "version": "8.58.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
+ "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
+ "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
+ "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
+ "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
+ "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils/node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.18.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/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/@vue/compiler-core": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz",
+ "integrity": "sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.25.3",
+ "@vue/shared": "3.5.11",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz",
+ "integrity": "sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==",
+ "dev": true,
+ "dependencies": {
+ "@vue/compiler-core": "3.5.11",
+ "@vue/shared": "3.5.11"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.11.tgz",
+ "integrity": "sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.25.3",
+ "@vue/compiler-core": "3.5.11",
+ "@vue/compiler-dom": "3.5.11",
+ "@vue/compiler-ssr": "3.5.11",
+ "@vue/shared": "3.5.11",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.11",
+ "postcss": "^8.4.47",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.11.tgz",
+ "integrity": "sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==",
+ "dev": true,
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.11",
+ "@vue/shared": "3.5.11"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz",
+ "integrity": "sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==",
+ "dev": true
+ },
+ "node_modules/@whatwg-node/promise-helpers": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
+ "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.6.3"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@wry/caches": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
+ "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/context": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
+ "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/equality": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
+ "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/trie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
+ "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/abstract-logging": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-db": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
+ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
+ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
+ "dependencies": {
+ "mime-db": "^1.53.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/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,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/all-node-versions": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/all-node-versions/-/all-node-versions-13.0.1.tgz",
+ "integrity": "sha512-5pG14FNgn5ClyGv8diB7uTcsmi2NWk9rDH+cGbVsqHjeqptegK0UfCsBA/vNUOZPNOPnYNzk31EM9OjJktld/g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "fetch-node-website": "^9.0.1",
+ "filter-obj": "^6.1.0",
+ "global-cache-dir": "^6.0.1",
+ "is-plain-obj": "^4.1.0",
+ "path-exists": "^5.0.0",
+ "semver": "^7.7.1",
+ "write-file-atomic": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/all-node-versions/node_modules/path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/all-node-versions/node_modules/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,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/all-node-versions/node_modules/write-file-atomic": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
+ "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ansicolors": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+ "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
+ "dev": true
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true
+ },
+ "node_modules/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,
+ "optional": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/apollo-upload-client": {
+ "version": "18.0.1",
+ "resolved": "https://registry.npmjs.org/apollo-upload-client/-/apollo-upload-client-18.0.1.tgz",
+ "integrity": "sha512-OQvZg1rK05VNI79D658FUmMdoI2oB/KJKb6QGMa2Si25QXOaAvLMBFUEwJct7wf+19U8vk9ILhidBOU1ZWv6QA==",
+ "dev": true,
+ "dependencies": {
+ "extract-files": "^13.0.0"
+ },
+ "engines": {
+ "node": "^18.15.0 || >=20.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jaydenseric"
+ },
+ "peerDependencies": {
+ "@apollo/client": "^3.8.0",
+ "graphql": "14 - 16"
+ }
+ },
+ "node_modules/app-module-path": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+ "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==",
+ "dev": true
+ },
+ "node_modules/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,
+ "dependencies": {
+ "default-require-extensions": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+ },
+ "node_modules/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
+ },
+ "node_modules/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
+ },
+ "node_modules/argv-formatter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz",
+ "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==",
+ "dev": true
+ },
+ "node_modules/array-ify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/assert-options": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.3.tgz",
+ "integrity": "sha512-s6v4HnA+vYSGO4eZX+F+I3gvF74wPk+m6Z1Q3w1Dsg4Pnv/R24vhKAasoMVZGvDpOOfTg1Qz4ptZnEbuy95XsQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/ast-module-types": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-6.0.0.tgz",
+ "integrity": "sha512-LFRg7178Fw5R4FAEwZxVqiRI8IxSM+Ay2UBrHoCerXNme+kMMMfz7T3xDGV/c2fer87hcrtgJGsnSOfUrPK6ng==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
+ },
+ "node_modules/async-retry": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
+ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+ "dependencies": {
+ "retry": "0.13.1"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "devOptional": true
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.17",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
+ "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
+ "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "core-js-compat": "^3.48.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
+ "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/backoff": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
+ "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
+ "dependencies": {
+ "precond": "0.2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.12",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz",
+ "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==",
+ "dev": true,
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/bcryptjs": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
+ "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
+ "bin": {
+ "bcrypt": "bin/bcrypt"
+ }
+ },
+ "node_modules/before-after-hook": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
+ "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="
+ },
+ "node_modules/body-parser": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.1",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/bottleneck": {
+ "version": "2.19.5",
+ "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
+ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bson": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz",
+ "integrity": "sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw==",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "node_modules/buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/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
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@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"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/cachedir": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "hasha": "^5.0.0",
+ "make-dir": "^3.0.0",
+ "package-hash": "^4.0.0",
+ "write-file-atomic": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/caching-transform/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caching-transform/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dev": true,
+ "dependencies": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001782",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001782.tgz",
+ "integrity": "sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/cardinal": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz",
+ "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==",
+ "dev": true,
+ "dependencies": {
+ "ansicolors": "~0.3.2",
+ "redeyed": "~2.1.0"
+ },
+ "bin": {
+ "cdl": "bin/cdl.js"
+ }
+ },
+ "node_modules/catharsis": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.15"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.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"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/clean-css": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+ "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+ "dev": true,
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/clean-jsdoc-theme": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.3.0.tgz",
+ "integrity": "sha512-QMrBdZ2KdPt6V2Ytg7dIt0/q32U4COpxvR0UDhPjRRKRL0o0MvRCR5YpY37/4rPF1SI1AYEKAWyof7ndCb/dzA==",
+ "dev": true,
+ "dependencies": {
+ "@jsdoc/salty": "^0.2.4",
+ "fs-extra": "^10.1.0",
+ "html-minifier-terser": "^7.2.0",
+ "klaw-sync": "^6.0.0",
+ "lodash": "^4.17.21",
+ "showdown": "^2.1.0"
+ },
+ "peerDependencies": {
+ "jsdoc": ">=3.x <=4.x"
+ }
+ },
+ "node_modules/clean-jsdoc-theme/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-highlight": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+ "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "highlight.js": "^10.7.1",
+ "mz": "^2.4.0",
+ "parse5": "^5.1.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.0",
+ "yargs": "^16.0.0"
+ },
+ "bin": {
+ "highlight": "bin/highlight"
+ },
+ "engines": {
+ "node": ">=8.0.0",
+ "npm": ">=5.0.0"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/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,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cli-highlight/node_modules/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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/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,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/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,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cli-highlight/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cli-progress": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.3"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+ "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^8.0.0",
+ "string-width": "^8.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/string-width": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
+ "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
+ "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
+ "dependencies": {
+ "color-convert": "^3.1.3",
+ "color-string": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/color-string": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
+ "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/color-string/node_modules/color-name": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/color/node_modules/color-convert": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
+ "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
+ "dependencies": {
+ "color-name": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.6"
+ }
+ },
+ "node_modules/color/node_modules/color-name": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/colors-option": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/colors-option/-/colors-option-6.0.1.tgz",
+ "integrity": "sha512-FsAlu5KTTN+W6Xc4NpxNAhl8iCKwVBzjL7Y2ZK6G9zMv50AfMDlU7Mi16lzaDK8Iwpoq/GfAXX+WrYx38gfSHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.4.1",
+ "is-plain-obj": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/colors-option/node_modules/chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "devOptional": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "node_modules/compare-func": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+ "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+ "dev": true,
+ "dependencies": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "node_modules/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=="
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/conventional-changelog-angular": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz",
+ "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==",
+ "dev": true,
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/conventional-changelog-writer": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz",
+ "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==",
+ "dev": true,
+ "dependencies": {
+ "@types/semver": "^7.5.5",
+ "conventional-commits-filter": "^5.0.0",
+ "handlebars": "^4.7.7",
+ "meow": "^13.0.0",
+ "semver": "^7.5.2"
+ },
+ "bin": {
+ "conventional-changelog-writer": "dist/cli/index.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/conventional-commits-filter": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz",
+ "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/conventional-commits-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz",
+ "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==",
+ "dev": true,
+ "dependencies": {
+ "meow": "^13.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "dist/cli/index.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/convert-hrtime": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz",
+ "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/cookie": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz",
+ "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.28.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-js-pure": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz",
+ "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/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==",
+ "dev": true
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/cross-inspect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
+ "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "devOptional": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+ },
+ "node_modules/crypto-random-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/crypto-random-string/node_modules/type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
+ "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz",
+ "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "decompress-tar": "^4.0.0",
+ "decompress-tarbz2": "^4.0.0",
+ "decompress-targz": "^4.0.0",
+ "decompress-unzip": "^4.0.1",
+ "graceful-fs": "^4.1.10",
+ "make-dir": "^1.0.0",
+ "pify": "^2.3.0",
+ "strip-dirs": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-tar": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz",
+ "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "file-type": "^5.2.0",
+ "is-stream": "^1.1.0",
+ "tar-stream": "^1.5.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress-tar/node_modules/is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress-tarbz2": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz",
+ "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "decompress-tar": "^4.1.0",
+ "file-type": "^6.1.0",
+ "is-stream": "^1.1.0",
+ "seek-bzip": "^1.0.5",
+ "unbzip2-stream": "^1.0.9"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress-tarbz2/node_modules/file-type": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz",
+ "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress-tarbz2/node_modules/is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress-targz": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz",
+ "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "decompress-tar": "^4.1.1",
+ "file-type": "^5.2.0",
+ "is-stream": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress-targz/node_modules/is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress-unzip": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
+ "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "file-type": "^3.8.0",
+ "get-stream": "^2.2.0",
+ "pify": "^2.3.0",
+ "yauzl": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress-unzip/node_modules/file-type": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+ "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress-unzip/node_modules/get-stream": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
+ "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.0.1",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress-unzip/node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decompress/node_modules/make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress/node_modules/make-dir/node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/decompress/node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/deep-diff": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz",
+ "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==",
+ "dev": true
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/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,
+ "dependencies": {
+ "strip-bom": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dependency-tree": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-11.0.1.tgz",
+ "integrity": "sha512-eCt7HSKIC9NxgIykG2DRq3Aewn9UhVS14MB3rEn6l/AsEI1FBg6ZGSlCU0SZ6Tjm2kkhj6/8c2pViinuyKELhg==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^12.0.0",
+ "filing-cabinet": "^5.0.1",
+ "precinct": "^12.0.2",
+ "typescript": "^5.4.5"
+ },
+ "bin": {
+ "dependency-tree": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/dependency-tree/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/deprecation": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
+ "dev": true
+ },
+ "node_modules/detective-amd": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-6.0.0.tgz",
+ "integrity": "sha512-NTqfYfwNsW7AQltKSEaWR66hGkTeD52Kz3eRQ+nfkA9ZFZt3iifRCWh+yZ/m6t3H42JFwVFTrml/D64R2PAIOA==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^6.0.0",
+ "escodegen": "^2.1.0",
+ "get-amd-module-type": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "bin": {
+ "detective-amd": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-cjs": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-6.0.0.tgz",
+ "integrity": "sha512-R55jTS6Kkmy6ukdrbzY4x+I7KkXiuDPpFzUViFV/tm2PBGtTCjkh9ZmTuJc1SaziMHJOe636dtiZLEuzBL9drg==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-es6": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-5.0.0.tgz",
+ "integrity": "sha512-NGTnzjvgeMW1khUSEXCzPDoraLenWbUjCFjwxReH+Ir+P6LGjYtaBbAvITWn2H0VSC+eM7/9LFOTAkrta6hNYg==",
+ "dev": true,
+ "dependencies": {
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-postcss": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-7.0.0.tgz",
+ "integrity": "sha512-pSXA6dyqmBPBuERpoOKKTUUjQCZwZPLRbd1VdsTbt6W+m/+6ROl4BbE87yQBUtLoK7yX8pvXHdKyM/xNIW9F7A==",
+ "dev": true,
+ "dependencies": {
+ "is-url": "^1.2.4",
+ "postcss-values-parser": "^6.0.2"
+ },
+ "engines": {
+ "node": "^14.0.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.38"
+ }
+ },
+ "node_modules/detective-sass": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-6.0.0.tgz",
+ "integrity": "sha512-h5GCfFMkPm4ZUUfGHVPKNHKT8jV7cSmgK+s4dgQH4/dIUNh9/huR1fjEQrblOQNDalSU7k7g+tiW9LJ+nVEUhg==",
+ "dev": true,
+ "dependencies": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-scss": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-5.0.0.tgz",
+ "integrity": "sha512-Y64HyMqntdsCh1qAH7ci95dk0nnpA29g319w/5d/oYcHolcGUVJbIhOirOFjfN1KnMAXAFm5FIkZ4l2EKFGgxg==",
+ "dev": true,
+ "dependencies": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-stylus": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-5.0.0.tgz",
+ "integrity": "sha512-KMHOsPY6aq3196WteVhkY5FF+6Nnc/r7q741E+Gq+Ax9mhE2iwj8Hlw8pl+749hPDRDBHZ2WlgOjP+twIG61vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/detective-typescript": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-13.0.0.tgz",
+ "integrity": "sha512-tcMYfiFWoUejSbvSblw90NDt76/4mNftYCX0SMnVRYzSXv8Fvo06hi4JOPdNvVNxRtCAKg3MJ3cBJh+ygEMH+A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "^7.6.0",
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": "^14.14.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "typescript": "^5.4.4"
+ }
+ },
+ "node_modules/detective-vue2": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/detective-vue2/-/detective-vue2-2.0.3.tgz",
+ "integrity": "sha512-AgWdSfVnft8uPGnUkdvE1EDadEENDCzoSRMt2xZfpxsjqVO617zGWXbB8TGIxHaqHz/nHa6lOSgAB8/dt0yEug==",
+ "dev": true,
+ "dependencies": {
+ "@vue/compiler-sfc": "^3.4.27",
+ "detective-es6": "^5.0.0",
+ "detective-sass": "^6.0.0",
+ "detective-scss": "^5.0.0",
+ "detective-stylus": "^5.0.0",
+ "detective-typescript": "^13.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "typescript": "^5.4.4"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/duplexify": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
+ }
+ },
+ "node_modules/duplexify/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "devOptional": true
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.328",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz",
+ "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==",
+ "dev": true
+ },
+ "node_modules/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=="
+ },
+ "node_modules/emojilib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
+ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
+ "dev": true
+ },
+ "node_modules/enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "devOptional": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "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",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-ci": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.2.0.tgz",
+ "integrity": "sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "execa": "^8.0.0",
+ "java-properties": "^1.0.2"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ }
+ },
+ "node_modules/env-ci/node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/env-ci/node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/env-ci/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-ci/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/env-ci/node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "license": "MIT"
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
+ "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.14.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.27.0",
+ "@eslint/plugin-kit": "^0.3.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-expect-type": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-expect-type/-/eslint-plugin-expect-type-0.6.2.tgz",
+ "integrity": "sha512-XWgtpplzr6GlpPUFG9ZApnSTv7QJXAPNN6hNmrlleVVCkAK23f/3E2BiCoA3Xtb0rIKfVKh7TLe+D1tcGt8/1w==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/utils": "^6.10.0 || ^7.0.1 || ^8",
+ "fs-extra": "^11.1.1",
+ "get-tsconfig": "^4.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": ">=6",
+ "eslint": ">=7",
+ "typescript": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-unused-imports": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz",
+ "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
+ "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/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,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint/node_modules/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,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/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,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/expo-server-sdk": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-6.1.0.tgz",
+ "integrity": "sha512-ISuax1AQ7cpM5RAqcu8gVcoLL0ZKskJ5OLoMWmdITBe9nYjTucjdGyBq817YkIvTcj1pAUwx+9toUT7l/V7thA==",
+ "license": "MIT",
+ "dependencies": {
+ "promise-limit": "^2.7.0",
+ "promise-retry": "^2.0.1",
+ "undici": "^7.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/expo-server-sdk/node_modules/undici": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz",
+ "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
+ "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "10.1.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/express/node_modules/mime-db": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
+ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/mime-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
+ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
+ "dependencies": {
+ "mime-db": "^1.53.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/extract-files": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-13.0.0.tgz",
+ "integrity": "sha512-FXD+2Tsr8Iqtm3QZy1Zmwscca7Jx3mMC5Crr+sEP1I303Jy1CYMuYCm7hRTplFNg3XdUavErkxnTzpaqdSoi6g==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-obj": "^4.1.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jaydenseric"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/farmhash-modern": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz",
+ "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/fast-content-type-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
+ "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/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=="
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/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
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
+ "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "path-expression-matcher": "^1.1.3"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.5.9",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz",
+ "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "fast-xml-builder": "^1.1.4",
+ "path-expression-matcher": "^1.2.0",
+ "strnum": "^2.2.2"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
+ "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+ },
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/fetch-node-website": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/fetch-node-website/-/fetch-node-website-9.0.1.tgz",
+ "integrity": "sha512-htQY+YRRFdMAxmQG8EpnVy32lQyXBjgFAvyfaaq7VCn53Py1gorggPMYAt1Zmp0AlNS1X/YnGt641RAkUbsETw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "cli-progress": "^3.12.0",
+ "colors-option": "^6.0.1",
+ "figures": "^6.0.1",
+ "got": "^13.0.0",
+ "is-plain-obj": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
+ "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-unicode-supported": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/figures/node_modules/is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/file-stream-rotator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
+ "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
+ "dependencies": {
+ "moment": "^2.29.1"
+ }
+ },
+ "node_modules/file-type": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
+ "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/filing-cabinet": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.0.2.tgz",
+ "integrity": "sha512-RZlFj8lzyu6jqtFBeXNqUjjNG6xm+gwXue3T70pRxw1W40kJwlgq0PSWAmh0nAnn5DHuBIecLXk9+1VKS9ICXA==",
+ "dev": true,
+ "dependencies": {
+ "app-module-path": "^2.2.0",
+ "commander": "^12.0.0",
+ "enhanced-resolve": "^5.16.0",
+ "module-definition": "^6.0.0",
+ "module-lookup-amd": "^9.0.1",
+ "resolve": "^1.22.8",
+ "resolve-dependency-path": "^4.0.0",
+ "sass-lookup": "^6.0.1",
+ "stylus-lookup": "^6.0.0",
+ "tsconfig-paths": "^4.2.0",
+ "typescript": "^5.4.4"
+ },
+ "bin": {
+ "filing-cabinet": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/filing-cabinet/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/filter-obj": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-6.1.0.tgz",
+ "integrity": "sha512-xdMtCAODmPloU9qtmPcdBV9Kd27NtMse+4ayThxqIHUES5Z2S6bGpap5PpdmNM56ub7y3i1eyr+vJJIIgWGKmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/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,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-cache-dir/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-up-simple": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
+ "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/find-versions": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz",
+ "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==",
+ "dev": true,
+ "dependencies": {
+ "semver-regex": "^4.0.5",
+ "super-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/firebase-admin": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.7.0.tgz",
+ "integrity": "sha512-o3qS8zCJbApe7aKzkO2Pa380t9cHISqeSd3blqYTtOuUUUua3qZTLwNWgGUOss3td6wbzrZhiHIj3c8+fC046Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@fastify/busboy": "^3.0.0",
+ "@firebase/database-compat": "^2.0.0",
+ "@firebase/database-types": "^1.0.6",
+ "farmhash-modern": "^1.1.0",
+ "fast-deep-equal": "^3.1.1",
+ "google-auth-library": "^10.6.1",
+ "jsonwebtoken": "^9.0.0",
+ "jwks-rsa": "^3.1.0",
+ "node-forge": "^1.3.1",
+ "uuid": "^11.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@google-cloud/firestore": "^7.11.0",
+ "@google-cloud/storage": "^7.19.0"
+ }
+ },
+ "node_modules/firebase-admin/node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true
+ },
+ "node_modules/fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.17"
+ }
+ },
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "node_modules/fromentries": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
+ "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/fs-capacitor": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz",
+ "integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "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==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function-timeout": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz",
+ "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/gauge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-5.0.1.tgz",
+ "integrity": "sha512-CmykPMJGuNan/3S4kZOpvvPYSNqSHANiWnh9XcMU2pSjtBfF0XzZ2p1bFAxTbnFxyBuPxQYHhzwaoOmUdqzvxQ==",
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^4.0.1",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/gauge/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gaxios": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9",
+ "uuid": "^9.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gaxios/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/gaxios/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/gaxios/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/gaxios/node_modules/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==",
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/gaxios/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz",
+ "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gcp-metadata/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-amd-module-type": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.0.tgz",
+ "integrity": "sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
+ "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": true
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
+ "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/git-log-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz",
+ "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==",
+ "dev": true,
+ "dependencies": {
+ "argv-formatter": "~1.0.0",
+ "spawn-error-forwarder": "~1.0.0",
+ "split2": "~1.0.0",
+ "stream-combiner2": "~1.1.1",
+ "through2": "~2.0.0",
+ "traverse": "~0.6.6"
+ }
+ },
+ "node_modules/git-log-parser/node_modules/split2": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz",
+ "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==",
+ "dev": true,
+ "dependencies": {
+ "through2": "~2.0.0"
+ }
+ },
+ "node_modules/git-log-parser/node_modules/through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/global-cache-dir": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/global-cache-dir/-/global-cache-dir-6.0.1.tgz",
+ "integrity": "sha512-HOOgvCW8le14HM0sTTvyYkTMRot7hq5ERIzNTUcDyZ4Vr9qF/IHUZeIcz4+v6vpwTFMqZ8QHKJYpXYRy/DSb6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cachedir": "^2.4.0",
+ "path-exists": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/global-cache-dir/node_modules/path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby/node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/gonzales-pe": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "gonzales": "bin/gonzales.js"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/google-auth-library": {
+ "version": "10.6.2",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz",
+ "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.1.4",
+ "gcp-metadata": "8.1.2",
+ "google-logging-utils": "1.1.3",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-auth-library/node_modules/gaxios": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz",
+ "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-auth-library/node_modules/gcp-metadata": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-gax": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz",
+ "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@grpc/grpc-js": "^1.10.9",
+ "@grpc/proto-loader": "^0.7.13",
+ "@types/long": "^4.0.0",
+ "abort-controller": "^3.0.0",
+ "duplexify": "^4.0.0",
+ "google-auth-library": "^9.3.0",
+ "node-fetch": "^2.7.0",
+ "object-hash": "^3.0.0",
+ "proto3-json-serializer": "^2.0.2",
+ "protobufjs": "^7.3.2",
+ "retry-request": "^7.0.0",
+ "uuid": "^9.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax/node_modules/gcp-metadata": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^6.1.1",
+ "google-logging-utils": "^0.0.2",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax/node_modules/google-auth-library": {
+ "version": "9.15.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^6.1.1",
+ "gcp-metadata": "^6.1.0",
+ "gtoken": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax/node_modules/google-logging-utils": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/google-gax/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/google-gax/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/google-gax/node_modules/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==",
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/google-gax/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
+ "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@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"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "node_modules/graphql": {
+ "version": "16.13.2",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz",
+ "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+ },
+ "node_modules/graphql-list-fields": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/graphql-list-fields/-/graphql-list-fields-2.0.4.tgz",
+ "integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
+ },
+ "node_modules/graphql-relay": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz",
+ "integrity": "sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw==",
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >= 15.9.0"
+ },
+ "peerDependencies": {
+ "graphql": "^16.2.0"
+ }
+ },
+ "node_modules/graphql-tag": {
+ "version": "2.12.6",
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+ "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+ }
+ },
+ "node_modules/graphql-upload": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-15.0.2.tgz",
+ "integrity": "sha512-ufJAkZJBKWRDD/4wJR3VZMy9QWTwqIYIciPtCEF5fCNgWF+V1p7uIgz+bP2YYLiS4OJBhCKR8rnqE/Wg3XPUiw==",
+ "dependencies": {
+ "@types/busboy": "^1.5.0",
+ "@types/node": "*",
+ "@types/object-path": "^0.11.1",
+ "busboy": "^1.6.0",
+ "fs-capacitor": "^6.2.0",
+ "http-errors": "^2.0.0",
+ "object-path": "^0.11.8"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jaydenseric"
+ },
+ "peerDependencies": {
+ "@types/express": "^4.0.29",
+ "@types/koa": "^2.11.4",
+ "graphql": "^16.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/express": {
+ "optional": true
+ },
+ "@types/koa": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/gtoken": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+ "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^6.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/handlebars": {
+ "version": "4.7.9",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+ },
+ "node_modules/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,
+ "dependencies": {
+ "is-stream": "^2.0.0",
+ "type-fest": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "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==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dev": true,
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hook-std": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
+ "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
+ "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/hosted-git-info/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/html-entities": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
+ "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/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
+ },
+ "node_modules/html-minifier-terser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+ "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+ "dev": true,
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "~5.3.2",
+ "commander": "^10.0.0",
+ "entities": "^4.4.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.15.1"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/html-minifier-terser/node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/http_ece": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
+ "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+ "license": "MIT"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/http2-wrapper": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+ "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/idb-keyval": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+ "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-from-esm": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz",
+ "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.20"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+ "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/index-to-position": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz",
+ "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/intersect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz",
+ "integrity": "sha512-qsc720yevCO+4NydrJWgEWKccAQwTOvj2m73O/VBA6iUL2HGZJ9XqBiyraNrBXX/W1IAjdpXdRZk24sq8TzBRg=="
+ },
+ "node_modules/into-stream": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz",
+ "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==",
+ "dev": true,
+ "dependencies": {
+ "from2": "^2.3.0",
+ "p-is-promise": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ip-address": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/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,
+ "optional": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/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==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-natural-number": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
+ "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-text-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+ "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+ "dev": true,
+ "dependencies": {
+ "text-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "dev": true
+ },
+ "node_modules/is-url-superb": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz",
+ "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "devOptional": true
+ },
+ "node_modules/issue-parser": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz",
+ "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==",
+ "dev": true,
+ "dependencies": {
+ "lodash.capitalize": "^4.2.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.uniqby": "^4.7.0"
+ },
+ "engines": {
+ "node": "^18.17 || >=20.6.1"
+ }
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "append-transform": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-processinfo/node_modules/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,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-processinfo/node_modules/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,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+ "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/iterall": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz",
+ "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "devOptional": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jasmine": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-6.1.0.tgz",
+ "integrity": "sha512-WPphPqEMY0uBRMjuhRHoVoxQNvJuxIMqz0yIcJ3k3oYxBedeGoH60/NXNgasxnx2FvfXrq5/r+2wssJ7WE8ABw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jasminejs/reporters": "^1.0.0",
+ "glob": "^10.2.2 || ^11.0.3 || ^12.0.0 || ^13.0.0",
+ "jasmine-core": "~6.1.0"
+ },
+ "bin": {
+ "jasmine": "bin/jasmine.js"
+ }
+ },
+ "node_modules/jasmine-core": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.1.0.tgz",
+ "integrity": "sha512-p/tjBw58O6vxKIWMlrU+yys8lqR3+l3UrqwNTT7wpj+dQ7N4etQekFM8joI+cWzPDYqZf54kN+hLC1+s5TvZvg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jasmine-spec-reporter": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz",
+ "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==",
+ "dev": true,
+ "dependencies": {
+ "colors": "1.4.0"
+ }
+ },
+ "node_modules/jasmine/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/jasmine/node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jasmine/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jasmine/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jasmine/node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/jasmine/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/java-properties": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz",
+ "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/jose": {
+ "version": "4.15.5",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz",
+ "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/js2xmlparser": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
+ "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
+ "dev": true,
+ "dependencies": {
+ "xmlcreate": "^2.0.4"
+ }
+ },
+ "node_modules/jsdoc": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz",
+ "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/parser": "^7.20.15",
+ "@jsdoc/salty": "^0.2.1",
+ "@types/markdown-it": "^14.1.1",
+ "bluebird": "^3.7.2",
+ "catharsis": "^0.9.0",
+ "escape-string-regexp": "^2.0.0",
+ "js2xmlparser": "^4.0.2",
+ "klaw": "^3.0.0",
+ "markdown-it": "^14.1.0",
+ "markdown-it-anchor": "^8.6.7",
+ "marked": "^4.0.10",
+ "mkdirp": "^1.0.4",
+ "requizzle": "^0.2.3",
+ "strip-json-comments": "^3.1.0",
+ "underscore": "~1.13.2"
+ },
+ "bin": {
+ "jsdoc": "jsdoc.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/jsdoc-babel": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsdoc-babel/-/jsdoc-babel-0.5.0.tgz",
+ "integrity": "sha512-PYfTbc3LNTeR8TpZs2M94NLDWqARq0r9gx3SvuziJfmJS7/AeMKvtj0xjzOX0R/4MOVA7/FqQQK7d6U0iEoztQ==",
+ "dev": true,
+ "dependencies": {
+ "jsdoc-regex": "^1.0.1",
+ "lodash": "^4.17.10"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/jsdoc-regex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/jsdoc-regex/-/jsdoc-regex-1.0.1.tgz",
+ "integrity": "sha512-CMFgT3K8GbmChWEfLWe6jlv9x33E8wLPzBjxIlh/eHLMcnDF+TF3CL265ZGBe029o1QdFepwVrQu0WuqqNPncg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/jsdoc/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "node_modules/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==",
+ "dev": true
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/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
+ },
+ "node_modules/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
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ]
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jwks-rsa": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz",
+ "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/express": "^4.17.20",
+ "@types/jsonwebtoken": "^9.0.4",
+ "debug": "^4.3.4",
+ "jose": "^4.15.4",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+ "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
+ "node_modules/kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+ },
+ "node_modules/ldapjs": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz",
+ "integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==",
+ "dependencies": {
+ "@ldapjs/asn1": "^2.0.0",
+ "@ldapjs/attribute": "^1.0.0",
+ "@ldapjs/change": "^1.0.0",
+ "@ldapjs/controls": "^2.1.0",
+ "@ldapjs/dn": "^1.1.0",
+ "@ldapjs/filter": "^2.1.1",
+ "@ldapjs/messages": "^1.3.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "abstract-logging": "^2.0.1",
+ "assert-plus": "^1.0.0",
+ "backoff": "^2.5.0",
+ "once": "^1.4.0",
+ "vasync": "^2.2.1",
+ "verror": "^1.10.1"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "dev": true,
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/lint-staged": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz",
+ "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^14.0.3",
+ "listr2": "^9.0.5",
+ "picomatch": "^4.0.3",
+ "string-argv": "^0.3.2",
+ "tinyexec": "^1.0.4",
+ "yaml": "^2.8.2"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=20.17"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
+ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^5.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/listr2/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dev": true,
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "license": "MIT"
+ },
+ "node_modules/lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "dev": true
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/lodash.capitalize": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
+ "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==",
+ "dev": true
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "node_modules/lodash.escaperegexp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+ "dev": true
+ },
+ "node_modules/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
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
+ "node_modules/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
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
+ "node_modules/lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
+ },
+ "node_modules/lodash.uniqby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
+ "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
+ "dev": true
+ },
+ "node_modules/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,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/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,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/logform": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "dependencies": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/logform/node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/loglevel": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
+ "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==",
+ "engines": {
+ "node": ">= 0.6.0"
+ },
+ "funding": {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/loglevel"
+ }
+ },
+ "node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/lru-memoizer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz",
+ "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==",
+ "dependencies": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "~4.0.0"
+ }
+ },
+ "node_modules/lru-memoizer/node_modules/lru-cache": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+ "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==",
+ "dependencies": {
+ "pseudomap": "^1.0.1",
+ "yallist": "^2.0.0"
+ }
+ },
+ "node_modules/lru-memoizer/node_modules/yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
+ },
+ "node_modules/m": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/m/-/m-1.10.0.tgz",
+ "integrity": "sha512-+vXing+uUyCeZZlY2RIteWHSPHgVcFyBoeWrBU5F3ibDt847sVPGHK41GriFP05uMvfHZkhlaAMYEHoQkfksvA==",
+ "dev": true,
+ "bin": {
+ "m": "bin/m"
+ }
+ },
+ "node_modules/madge": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/madge/-/madge-8.0.0.tgz",
+ "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "commander": "^7.2.0",
+ "commondir": "^1.0.1",
+ "debug": "^4.3.4",
+ "dependency-tree": "^11.0.0",
+ "ora": "^5.4.1",
+ "pluralize": "^8.0.0",
+ "pretty-ms": "^7.0.1",
+ "rc": "^1.2.8",
+ "stream-to-array": "^2.3.0",
+ "ts-graphviz": "^2.1.2",
+ "walkdir": "^0.4.1"
+ },
+ "bin": {
+ "madge": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://www.paypal.me/pahen"
+ },
+ "peerDependencies": {
+ "typescript": "^5.4.4"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/madge/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/madge/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/madge/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/madge/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/madge/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/madge/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/madge/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.11",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+ "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it-anchor": {
+ "version": "8.6.7",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
+ "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
+ "dev": true,
+ "peerDependencies": {
+ "@types/markdown-it": "*",
+ "markdown-it": "*"
+ }
+ },
+ "node_modules/marked": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "dev": true,
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/marked-terminal": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz",
+ "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "ansi-regex": "^6.1.0",
+ "chalk": "^5.4.1",
+ "cli-highlight": "^2.1.11",
+ "cli-table3": "^0.6.5",
+ "node-emoji": "^2.2.0",
+ "supports-hyperlinks": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "marked": ">=1 <16"
+ }
+ },
+ "node_modules/marked-terminal/node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/marked-terminal/node_modules/chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "dev": true
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/memory-pager": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
+ },
+ "node_modules/meow": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+ "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+ "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
+ "funding": [
+ "https://github.com/sponsors/broofa"
+ ],
+ "bin": {
+ "mime": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "devOptional": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "devOptional": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mock-files-adapter": {
+ "resolved": "spec/dependencies/mock-files-adapter",
+ "link": true
+ },
+ "node_modules/mock-mail-adapter": {
+ "resolved": "spec/dependencies/mock-mail-adapter",
+ "link": true
+ },
+ "node_modules/module-definition": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-6.0.0.tgz",
+ "integrity": "sha512-sEGP5nKEXU7fGSZUML/coJbrO+yQtxcppDAYWRE9ovWsTbFoUHB2qDUx564WUzDaBHXsD46JBbIK5WVTwCyu3w==",
+ "dev": true,
+ "dependencies": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ },
+ "bin": {
+ "module-definition": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/module-lookup-amd": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-9.0.2.tgz",
+ "integrity": "sha512-p7PzSVEWiW9fHRX9oM+V4aV5B2nCVddVNv4DZ/JB6t9GsXY4E+ZVhPpnwUX7bbJyGeeVZqhS8q/JZ/H77IqPFA==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^12.1.0",
+ "glob": "^7.2.3",
+ "requirejs": "^2.3.7",
+ "requirejs-config-file": "^4.0.0"
+ },
+ "bin": {
+ "lookup-amd": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/module-lookup-amd/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mongodb": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.0.tgz",
+ "integrity": "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@mongodb-js/saslprep": "^1.3.0",
+ "bson": "^7.1.1",
+ "mongodb-connection-string-url": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.806.0",
+ "@mongodb-js/zstd": "^7.0.0",
+ "gcp-metadata": "^7.0.1",
+ "kerberos": "^7.0.0",
+ "mongodb-client-encryption": ">=7.0.0 <7.1.0",
+ "snappy": "^7.3.2",
+ "socks": "^2.8.6"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "gcp-metadata": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ },
+ "socks": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-connection-string-url": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
+ "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
+ "dev": true,
+ "dependencies": {
+ "@types/whatwg-url": "^11.0.2",
+ "whatwg-url": "^14.1.0 || ^13.0.0"
+ }
+ },
+ "node_modules/mongodb-download-url": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.6.2.tgz",
+ "integrity": "sha512-89g7A+ktFQ6L3fcjV1ClCj5ftlMSuVy22q76N6vhuzxBdYcD2O0Wxt+i16SQ7BAD1QtOPsGpSQVL4bUtLvY6+w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "minimist": "^1.2.8",
+ "node-fetch": "^2.7.0",
+ "semver": "^7.7.1"
+ },
+ "bin": {
+ "mongodb-download-url": "bin/mongodb-download-url.js"
+ }
+ },
+ "node_modules/mongodb-download-url/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-download-url/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mongodb-download-url/node_modules/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==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/mongodb-download-url/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/mongodb-runner": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.9.3.tgz",
+ "integrity": "sha512-2n2fCyUITi0UrAs0eg/zLSehSVOoWWUsgJleEBh6p1otHaiqMSAMURS6W7PLJvvGxFlnO3tjiDB6T11gjqAkUQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@mongodb-js/mongodb-downloader": "^0.4.2",
+ "@mongodb-js/saslprep": "^1.3.0",
+ "debug": "^4.4.0",
+ "mongodb": "^6.9.0",
+ "mongodb-connection-string-url": "^3.0.0",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "mongodb-runner": "bin/runner.js"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/bson": {
+ "version": "6.10.4",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
+ "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.20.1"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/mongodb-runner/node_modules/gaxios": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
+ "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/gcp-metadata": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
+ "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "gaxios": "^5.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/mongodb": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz",
+ "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==",
+ "dev": true,
+ "dependencies": {
+ "@mongodb-js/saslprep": "^1.3.0",
+ "bson": "^6.10.4",
+ "mongodb-connection-string-url": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=16.20.1"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.188.0",
+ "@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
+ "gcp-metadata": "^5.2.0",
+ "kerberos": "^2.0.1",
+ "mongodb-client-encryption": ">=6.0.0 <7",
+ "snappy": "^7.3.2",
+ "socks": "^2.7.1"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "gcp-metadata": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ },
+ "socks": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/mongodb-runner/node_modules/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==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/mongodb-runner/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/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,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mongodb-runner/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mongodb/node_modules/@types/whatwg-url": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
+ "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
+ "dependencies": {
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "node_modules/mongodb/node_modules/mongodb-connection-string-url": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
+ "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
+ "dependencies": {
+ "@types/whatwg-url": "^13.0.0",
+ "whatwg-url": "^14.1.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+ "bin": {
+ "mustache": "bin/mustache"
+ }
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "node_modules/nerf-dart": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz",
+ "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==",
+ "dev": true
+ },
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/node-abort-controller": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
+ "dev": true
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-emoji": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
+ "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^4.6.0",
+ "char-regex": "^1.0.2",
+ "emojilib": "^2.4.0",
+ "skin-tone": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/node-emoji/node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/node-forge": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "process-on-spawn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.36",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
+ "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
+ "dev": true
+ },
+ "node_modules/node-source-walk": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-7.0.0.tgz",
+ "integrity": "sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/normalize-package-data": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
+ "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
+ "dev": true,
+ "dependencies": {
+ "hosted-git-info": "^7.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/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,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
+ "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm": {
+ "version": "10.8.1",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz",
+ "integrity": "sha512-Dp1C6SvSMYQI7YHq/y2l94uvI+59Eqbu1EpuKQHQ8p16txXRuRit5gH3Lnaagk2aXDIjg/Iru9pd05bnneKgdw==",
+ "bundleDependencies": [
+ "@isaacs/string-locale-compare",
+ "@npmcli/arborist",
+ "@npmcli/config",
+ "@npmcli/fs",
+ "@npmcli/map-workspaces",
+ "@npmcli/package-json",
+ "@npmcli/promise-spawn",
+ "@npmcli/redact",
+ "@npmcli/run-script",
+ "@sigstore/tuf",
+ "abbrev",
+ "archy",
+ "cacache",
+ "chalk",
+ "ci-info",
+ "cli-columns",
+ "fastest-levenshtein",
+ "fs-minipass",
+ "glob",
+ "graceful-fs",
+ "hosted-git-info",
+ "ini",
+ "init-package-json",
+ "is-cidr",
+ "json-parse-even-better-errors",
+ "libnpmaccess",
+ "libnpmdiff",
+ "libnpmexec",
+ "libnpmfund",
+ "libnpmhook",
+ "libnpmorg",
+ "libnpmpack",
+ "libnpmpublish",
+ "libnpmsearch",
+ "libnpmteam",
+ "libnpmversion",
+ "make-fetch-happen",
+ "minimatch",
+ "minipass",
+ "minipass-pipeline",
+ "ms",
+ "node-gyp",
+ "nopt",
+ "normalize-package-data",
+ "npm-audit-report",
+ "npm-install-checks",
+ "npm-package-arg",
+ "npm-pick-manifest",
+ "npm-profile",
+ "npm-registry-fetch",
+ "npm-user-validate",
+ "p-map",
+ "pacote",
+ "parse-conflict-json",
+ "proc-log",
+ "qrcode-terminal",
+ "read",
+ "semver",
+ "spdx-expression-parse",
+ "ssri",
+ "supports-color",
+ "tar",
+ "text-table",
+ "tiny-relative-date",
+ "treeverse",
+ "validate-npm-package-name",
+ "which",
+ "write-file-atomic"
+ ],
+ "dev": true,
+ "dependencies": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/config": "^8.3.3",
+ "@npmcli/fs": "^3.1.1",
+ "@npmcli/map-workspaces": "^3.0.6",
+ "@npmcli/package-json": "^5.1.1",
+ "@npmcli/promise-spawn": "^7.0.2",
+ "@npmcli/redact": "^2.0.0",
+ "@npmcli/run-script": "^8.1.0",
+ "@sigstore/tuf": "^2.3.4",
+ "abbrev": "^2.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^18.0.3",
+ "chalk": "^5.3.0",
+ "ci-info": "^4.0.0",
+ "cli-columns": "^4.0.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^10.4.1",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^7.0.2",
+ "ini": "^4.1.3",
+ "init-package-json": "^6.0.3",
+ "is-cidr": "^5.1.0",
+ "json-parse-even-better-errors": "^3.0.2",
+ "libnpmaccess": "^8.0.6",
+ "libnpmdiff": "^6.1.3",
+ "libnpmexec": "^8.1.2",
+ "libnpmfund": "^5.0.11",
+ "libnpmhook": "^10.0.5",
+ "libnpmorg": "^6.0.6",
+ "libnpmpack": "^7.0.3",
+ "libnpmpublish": "^9.0.9",
+ "libnpmsearch": "^7.0.6",
+ "libnpmteam": "^6.0.5",
+ "libnpmversion": "^6.0.3",
+ "make-fetch-happen": "^13.0.1",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.1",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^10.1.0",
+ "nopt": "^7.2.1",
+ "normalize-package-data": "^6.0.1",
+ "npm-audit-report": "^5.0.0",
+ "npm-install-checks": "^6.3.0",
+ "npm-package-arg": "^11.0.2",
+ "npm-pick-manifest": "^9.0.1",
+ "npm-profile": "^10.0.0",
+ "npm-registry-fetch": "^17.0.1",
+ "npm-user-validate": "^2.0.1",
+ "p-map": "^4.0.0",
+ "pacote": "^18.0.6",
+ "parse-conflict-json": "^3.0.1",
+ "proc-log": "^4.2.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^3.0.1",
+ "semver": "^7.6.2",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^10.0.6",
+ "supports-color": "^9.4.0",
+ "tar": "^6.2.1",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^1.3.0",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^5.0.1",
+ "which": "^4.0.0",
+ "write-file-atomic": "^5.0.1"
+ },
+ "bin": {
+ "npm": "bin/npm-cli.js",
+ "npx": "bin/npx-cli.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/@npmcli/agent": {
+ "version": "2.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^10.0.1",
+ "socks-proxy-agent": "^8.0.3"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/arborist": {
+ "version": "7.5.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^3.1.1",
+ "@npmcli/installed-package-contents": "^2.1.0",
+ "@npmcli/map-workspaces": "^3.0.2",
+ "@npmcli/metavuln-calculator": "^7.1.1",
+ "@npmcli/name-from-folder": "^2.0.0",
+ "@npmcli/node-gyp": "^3.0.0",
+ "@npmcli/package-json": "^5.1.0",
+ "@npmcli/query": "^3.1.0",
+ "@npmcli/redact": "^2.0.0",
+ "@npmcli/run-script": "^8.1.0",
+ "bin-links": "^4.0.4",
+ "cacache": "^18.0.3",
+ "common-ancestor-path": "^1.0.1",
+ "hosted-git-info": "^7.0.2",
+ "json-parse-even-better-errors": "^3.0.2",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^10.2.2",
+ "minimatch": "^9.0.4",
+ "nopt": "^7.2.1",
+ "npm-install-checks": "^6.2.0",
+ "npm-package-arg": "^11.0.2",
+ "npm-pick-manifest": "^9.0.1",
+ "npm-registry-fetch": "^17.0.1",
+ "pacote": "^18.0.6",
+ "parse-conflict-json": "^3.0.0",
+ "proc-log": "^4.2.0",
+ "proggy": "^2.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.7",
+ "ssri": "^10.0.6",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^3.0.1"
+ },
+ "bin": {
+ "arborist": "bin/index.js"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/config": {
+ "version": "8.3.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/map-workspaces": "^3.0.2",
+ "ci-info": "^4.0.0",
+ "ini": "^4.1.2",
+ "nopt": "^7.2.1",
+ "proc-log": "^4.2.0",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.5",
+ "walk-up-path": "^3.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/fs": {
+ "version": "3.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/git": {
+ "version": "5.0.7",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/promise-spawn": "^7.0.0",
+ "lru-cache": "^10.0.1",
+ "npm-pick-manifest": "^9.0.0",
+ "proc-log": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^4.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/installed-package-contents": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-bundled": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ },
+ "bin": {
+ "installed-package-contents": "bin/index.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/map-workspaces": {
+ "version": "3.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/name-from-folder": "^2.0.0",
+ "glob": "^10.2.2",
+ "minimatch": "^9.0.0",
+ "read-package-json-fast": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
+ "version": "7.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cacache": "^18.0.0",
+ "json-parse-even-better-errors": "^3.0.0",
+ "pacote": "^18.0.0",
+ "proc-log": "^4.1.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/name-from-folder": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/node-gyp": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/package-json": {
+ "version": "5.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^5.0.0",
+ "glob": "^10.2.2",
+ "hosted-git-info": "^7.0.0",
+ "json-parse-even-better-errors": "^3.0.0",
+ "normalize-package-data": "^6.0.0",
+ "proc-log": "^4.0.0",
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/promise-spawn": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "which": "^4.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/query": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/redact": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@npmcli/run-script": {
+ "version": "8.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/node-gyp": "^3.0.0",
+ "@npmcli/package-json": "^5.0.0",
+ "@npmcli/promise-spawn": "^7.0.0",
+ "node-gyp": "^10.0.0",
+ "proc-log": "^4.0.0",
+ "which": "^4.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/bundle": {
+ "version": "2.3.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.3.2"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/core": {
+ "version": "1.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/protobuf-specs": {
+ "version": "0.3.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/sign": {
+ "version": "2.3.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.0.0",
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "make-fetch-happen": "^13.0.1",
+ "proc-log": "^4.2.0",
+ "promise-retry": "^2.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/tuf": {
+ "version": "2.3.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "tuf-js": "^2.2.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@sigstore/verify": {
+ "version": "1.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.1.0",
+ "@sigstore/protobuf-specs": "^0.3.2"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/@tufjs/models": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^9.0.4"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/abbrev": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/agent-base": {
+ "version": "7.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/npm/node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/aproba": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/archy": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/bin-links": {
+ "version": "4.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cmd-shim": "^6.0.0",
+ "npm-normalize-package-bin": "^3.0.0",
+ "read-cmd-shim": "^4.0.0",
+ "write-file-atomic": "^5.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/cacache": {
+ "version": "18.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^3.1.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^10.2.2",
+ "lru-cache": "^10.0.1",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^4.0.0",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^3.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/chalk": {
+ "version": "5.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/chownr": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm/node_modules/ci-info": {
+ "version": "4.0.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/cidr-regex": {
+ "version": "4.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "ip-regex": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/npm/node_modules/clean-stack": {
+ "version": "2.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/npm/node_modules/cli-columns": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/npm/node_modules/cmd-shim": {
+ "version": "6.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/common-ancestor-path": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/npm/node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/npm/node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/npm/node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/diff": {
+ "version": "5.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/npm/node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/encoding": {
+ "version": "0.1.13",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/npm/node_modules/env-paths": {
+ "version": "2.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/npm/node_modules/err-code": {
+ "version": "2.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/exponential-backoff": {
+ "version": "3.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/npm/node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/npm/node_modules/foreground-child": {
+ "version": "3.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/fs-minipass": {
+ "version": "3.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/function-bind": {
+ "version": "1.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/npm/node_modules/glob": {
+ "version": "10.4.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/hasown": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/npm/node_modules/hosted-git-info": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/npm/node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/npm/node_modules/https-proxy-agent": {
+ "version": "7.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.0.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/npm/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm/node_modules/ignore-walk": {
+ "version": "6.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minimatch": "^9.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/npm/node_modules/indent-string": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/ini": {
+ "version": "4.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/init-package-json": {
+ "version": "6.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/package-json": "^5.0.0",
+ "npm-package-arg": "^11.0.0",
+ "promzard": "^1.0.0",
+ "read": "^3.0.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4",
+ "validate-npm-package-name": "^5.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/ip-address": {
+ "version": "9.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsbn": "1.1.0",
+ "sprintf-js": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/npm/node_modules/ip-regex": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm/node_modules/is-cidr": {
+ "version": "5.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "cidr-regex": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/npm/node_modules/is-core-module": {
+ "version": "2.13.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/npm/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/is-lambda": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/isexe": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/jackspeak": {
+ "version": "3.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/npm/node_modules/jsbn": {
+ "version": "1.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/json-parse-even-better-errors": {
+ "version": "3.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/json-stringify-nice": {
+ "version": "1.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/jsonparse": {
+ "version": "1.3.1",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/just-diff": {
+ "version": "6.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/just-diff-apply": {
+ "version": "5.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/libnpmaccess": {
+ "version": "8.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-package-arg": "^11.0.2",
+ "npm-registry-fetch": "^17.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmdiff": {
+ "version": "6.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/installed-package-contents": "^2.1.0",
+ "binary-extensions": "^2.3.0",
+ "diff": "^5.1.0",
+ "minimatch": "^9.0.4",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6",
+ "tar": "^6.2.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmexec": {
+ "version": "8.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/run-script": "^8.1.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6",
+ "proc-log": "^4.2.0",
+ "read": "^3.0.1",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.7",
+ "walk-up-path": "^3.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmfund": {
+ "version": "5.0.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^7.5.3"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmhook": {
+ "version": "10.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmorg": {
+ "version": "6.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmpack": {
+ "version": "7.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/run-script": "^8.1.0",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmpublish": {
+ "version": "9.0.9",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "ci-info": "^4.0.0",
+ "normalize-package-data": "^6.0.1",
+ "npm-package-arg": "^11.0.2",
+ "npm-registry-fetch": "^17.0.1",
+ "proc-log": "^4.2.0",
+ "semver": "^7.3.7",
+ "sigstore": "^2.2.0",
+ "ssri": "^10.0.6"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmsearch": {
+ "version": "7.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^17.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmteam": {
+ "version": "6.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/libnpmversion": {
+ "version": "6.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^5.0.7",
+ "@npmcli/run-script": "^8.1.0",
+ "json-parse-even-better-errors": "^3.0.2",
+ "proc-log": "^4.2.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/lru-cache": {
+ "version": "10.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "14 || >=16.14"
+ }
+ },
+ "node_modules/npm/node_modules/make-fetch-happen": {
+ "version": "13.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/agent": "^2.0.0",
+ "cacache": "^18.0.0",
+ "http-cache-semantics": "^4.1.1",
+ "is-lambda": "^1.0.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^3.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "proc-log": "^4.2.0",
+ "promise-retry": "^2.0.1",
+ "ssri": "^10.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/minimatch": {
+ "version": "9.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/minipass": {
+ "version": "7.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-collect": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-fetch": {
+ "version": "3.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.0.3",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.13"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-json-stream": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-sized": {
+ "version": "1.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/minizlib": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/npm/node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm/node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/mute-stream": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/negotiator": {
+ "version": "0.6.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/npm/node_modules/node-gyp": {
+ "version": "10.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "glob": "^10.3.10",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^13.0.0",
+ "nopt": "^7.0.0",
+ "proc-log": "^3.0.0",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^4.0.0"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/nopt": {
+ "version": "7.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^2.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/normalize-package-data": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^7.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-audit-report": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-bundled": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-normalize-package-bin": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-install-checks": {
+ "version": "6.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-normalize-package-bin": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-package-arg": {
+ "version": "11.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^7.0.0",
+ "proc-log": "^4.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^5.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-packlist": {
+ "version": "8.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "ignore-walk": "^6.0.4"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-pick-manifest": {
+ "version": "9.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-install-checks": "^6.0.0",
+ "npm-normalize-package-bin": "^3.0.0",
+ "npm-package-arg": "^11.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-profile": {
+ "version": "10.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^17.0.1",
+ "proc-log": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-registry-fetch": {
+ "version": "17.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/redact": "^2.0.0",
+ "make-fetch-happen": "^13.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^3.0.0",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^11.0.0",
+ "proc-log": "^4.0.0"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/npm-user-validate": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/p-map": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm/node_modules/pacote": {
+ "version": "18.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^5.0.0",
+ "@npmcli/installed-package-contents": "^2.0.1",
+ "@npmcli/package-json": "^5.1.0",
+ "@npmcli/promise-spawn": "^7.0.0",
+ "@npmcli/run-script": "^8.0.0",
+ "cacache": "^18.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^11.0.0",
+ "npm-packlist": "^8.0.0",
+ "npm-pick-manifest": "^9.0.0",
+ "npm-registry-fetch": "^17.0.0",
+ "proc-log": "^4.0.0",
+ "promise-retry": "^2.0.1",
+ "sigstore": "^2.2.0",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11"
+ },
+ "bin": {
+ "pacote": "bin/index.js"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/parse-conflict-json": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/path-scurry": {
+ "version": "1.11.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/postcss-selector-parser": {
+ "version": "6.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm/node_modules/proc-log": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/proggy": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/promise-all-reject-late": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/promise-call-limit": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/promise-inflight": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/promise-retry": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm/node_modules/promzard": {
+ "version": "1.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "read": "^3.0.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/qrcode-terminal": {
+ "version": "0.12.0",
+ "dev": true,
+ "inBundle": true,
+ "bin": {
+ "qrcode-terminal": "bin/qrcode-terminal.js"
+ }
+ },
+ "node_modules/npm/node_modules/read": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "mute-stream": "^1.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/read-cmd-shim": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/read-package-json-fast": {
+ "version": "3.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/retry": {
+ "version": "0.12.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/npm/node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/npm/node_modules/semver": {
+ "version": "7.6.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm/node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm/node_modules/sigstore": {
+ "version": "2.3.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.0.0",
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "@sigstore/sign": "^2.3.2",
+ "@sigstore/tuf": "^2.3.4",
+ "@sigstore/verify": "^1.2.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/socks": {
+ "version": "2.8.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^9.0.5",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/socks-proxy-agent": {
+ "version": "8.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.1",
+ "debug": "^4.3.4",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/npm/node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/spdx-exceptions": {
+ "version": "2.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/npm/node_modules/spdx-expression-parse": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/spdx-license-ids": {
+ "version": "3.0.18",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/npm/node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/npm/node_modules/ssri": {
+ "version": "10.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/supports-color": {
+ "version": "9.4.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/tar": {
+ "version": "6.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm/node_modules/tar/node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/tar/node_modules/minipass": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm/node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/tiny-relative-date": {
+ "version": "1.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/treeverse": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/tuf-js": {
+ "version": "2.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/models": "2.0.1",
+ "debug": "^4.3.4",
+ "make-fetch-happen": "^13.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/unique-filename": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "unique-slug": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/unique-slug": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/validate-npm-package-name": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/walk-up-path": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npm/node_modules/which": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/which/node_modules/isexe": {
+ "version": "3.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "5.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/npm/node_modules/write-file-atomic": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/npmlog": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-7.0.1.tgz",
+ "integrity": "sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg==",
+ "dependencies": {
+ "are-we-there-yet": "^4.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^5.0.0",
+ "set-blocking": "^2.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npmlog/node_modules/are-we-there-yet": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz",
+ "integrity": "sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg==",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/nyc": {
+ "version": "17.1.0",
+ "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz",
+ "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==",
+ "dev": true,
+ "dependencies": {
+ "@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": "^3.3.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": "^6.0.2",
+ "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"
+ },
+ "bin": {
+ "nyc": "bin/nyc.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/nyc/node_modules/foreground-child/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nyc/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/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==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-path": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz",
+ "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==",
+ "engines": {
+ "node": ">= 10.12.0"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "dependencies": {
+ "fn.name": "1.x.x"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optimism": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz",
+ "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==",
+ "dev": true,
+ "dependencies": {
+ "@wry/caches": "^1.0.0",
+ "@wry/context": "^0.7.0",
+ "@wry/trie": "^0.4.3",
+ "tslib": "^2.3.0"
+ }
+ },
+ "node_modules/optimism/node_modules/@wry/trie": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz",
+ "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ora/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/ora/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/otpauth": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.5.0.tgz",
+ "integrity": "sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/hectorm/otpauth?sponsor=1"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/p-each-series": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz",
+ "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-filter": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz",
+ "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==",
+ "dev": true,
+ "dependencies": {
+ "p-map": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-filter/node_modules/p-map": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz",
+ "integrity": "sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-is-promise": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz",
+ "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "devOptional": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-reduce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz",
+ "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "graceful-fs": "^4.1.15",
+ "hasha": "^5.0.0",
+ "lodash.flattendeep": "^4.4.0",
+ "release-zalgo": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "devOptional": true
+ },
+ "node_modules/param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.6.0.tgz",
+ "integrity": "sha512-AZjc8yGo8/iTZFpCXWw/r1qNusiUGWtq9i92/u0jNd+Iupg3EJUSV/OOyTrCeav8NDyo92wVS5O3iKAYPlhlsA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "7.29.2",
+ "@babel/runtime-corejs3": "7.29.2",
+ "crypto-js": "4.2.0",
+ "idb-keyval": "6.2.2",
+ "react-native-crypto-js": "1.0.0",
+ "ws": "8.20.0"
+ },
+ "engines": {
+ "node": ">=20.19.0 <21 || >=22.13.0 <23 || >=24.1.0 <25"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
+ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz",
+ "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "devOptional": true,
+ "engines": {
+ "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.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "devOptional": true,
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "devOptional": true,
+ "license": "ISC"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+ "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pg": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
+ "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.11.0",
+ "pg-pool": "^3.11.0",
+ "pg-protocol": "^1.11.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.3.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
+ "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz",
+ "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==",
+ "license": "MIT"
+ },
+ "node_modules/pg-cursor": {
+ "version": "2.17.0",
+ "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz",
+ "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "pg": "^8"
+ }
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-minify": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.8.0.tgz",
+ "integrity": "sha512-jO/oJOununpx8DzKgvSsWm61P8JjwXlaxSlbbfTBo1nvSWoo/+I6qZYaSN96jm/KDwa5d+JMQwPGgcP6HXDRow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/pg-monitor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-3.1.0.tgz",
+ "integrity": "sha512-giK0h52AOO/v8iu6hZCdZ/X9W8oAM9Dm1VReQQtki532X8g4z1LVIm4Z/3cGvDcETWW+Ty0FrtU8iTrGFYIZfA==",
+ "dependencies": {
+ "picocolors": "1.1.1"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz",
+ "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-promise": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-12.6.0.tgz",
+ "integrity": "sha512-ZnfNn7c0U2p1OWYqoENcke8eSTe+yCGOpuMurExTuot/pe3POodbakE9Sj5MWUsyPpyreARUUPwe4j/5Dfs9Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "assert-options": "0.8.3",
+ "pg": "8.18.0",
+ "pg-minify": "1.8.0",
+ "spex": "4.1.0"
+ },
+ "engines": {
+ "node": ">=16.0"
+ },
+ "peerDependencies": {
+ "pg-query-stream": "4.12.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz",
+ "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==",
+ "license": "MIT"
+ },
+ "node_modules/pg-query-stream": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.12.0.tgz",
+ "integrity": "sha512-H97oiVPQ0+eRqIFOeYMUnjDcv9od7vHHMjiVDAhg2SEzAUr3M/dT83UEV1B+fm+tcVnymI8j2LSp57/+yjF6Fg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "pg-cursor": "^2.17.0"
+ },
+ "peerDependencies": {
+ "pg": "^8"
+ }
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pkg-conf": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz",
+ "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^2.0.0",
+ "load-json-file": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-conf/node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-values-parser": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz",
+ "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "^1.1.4",
+ "is-url-superb": "^4.0.0",
+ "quote-unquote": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.9"
+ }
+ },
+ "node_modules/postcss-values-parser/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/precinct": {
+ "version": "12.1.2",
+ "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz",
+ "integrity": "sha512-x2qVN3oSOp3D05ihCd8XdkIPuEQsyte7PSxzLqiRgktu79S5Dr1I75/S+zAup8/0cwjoiJTQztE9h0/sWp9bJQ==",
+ "dev": true,
+ "dependencies": {
+ "@dependents/detective-less": "^5.0.0",
+ "commander": "^12.1.0",
+ "detective-amd": "^6.0.0",
+ "detective-cjs": "^6.0.0",
+ "detective-es6": "^5.0.0",
+ "detective-postcss": "^7.0.0",
+ "detective-sass": "^6.0.0",
+ "detective-scss": "^5.0.0",
+ "detective-stylus": "^5.0.0",
+ "detective-typescript": "^13.0.0",
+ "detective-vue2": "^2.0.3",
+ "module-definition": "^6.0.0",
+ "node-source-walk": "^7.0.0",
+ "postcss": "^8.4.40",
+ "typescript": "^5.5.4"
+ },
+ "bin": {
+ "precinct": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/precinct/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/precond": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
+ "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/pretty-ms": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
+ "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
+ "dev": true,
+ "dependencies": {
+ "parse-ms": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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==",
+ "dev": true
+ },
+ "node_modules/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,
+ "dependencies": {
+ "fromentries": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/process-warning": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz",
+ "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA=="
+ },
+ "node_modules/promise-limit": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
+ "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==",
+ "license": "ISC"
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/promise-retry/node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true
+ },
+ "node_modules/proto3-json-serializer": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
+ "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "protobufjs": "^7.2.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
+ "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/protobufjs/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0",
+ "optional": true
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/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,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/quote-unquote": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz",
+ "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==",
+ "dev": true
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/rate-limit-redis": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.3.1.tgz",
+ "integrity": "sha512-+a1zU8+D7L8siDK9jb14refQXz60vq427VuiplgnaLk9B2LnvGe/APLTfhwb4uNIL7eWVknh8GnRp/unCj+lMA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "peerDependencies": {
+ "express-rate-limit": ">= 6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/raw-body/node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/raw-body/node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true
+ },
+ "node_modules/react-native-crypto-js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+ "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+ },
+ "node_modules/read-package-up": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
+ "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up-simple": "^1.0.0",
+ "read-pkg": "^9.0.0",
+ "type-fest": "^4.6.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
+ "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
+ "dev": true,
+ "dependencies": {
+ "@types/normalize-package-data": "^2.4.3",
+ "normalize-package-data": "^6.0.0",
+ "parse-json": "^8.0.0",
+ "type-fest": "^4.6.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg-up": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz",
+ "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==",
+ "deprecated": "Renamed to read-package-up",
+ "dev": true,
+ "dependencies": {
+ "find-up-simple": "^1.0.0",
+ "read-pkg": "^9.0.0",
+ "type-fest": "^4.6.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg/node_modules/parse-json": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz",
+ "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "index-to-position": "^0.1.2",
+ "type-fest": "^4.7.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "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"
+ }
+ },
+ "node_modules/readable-stream/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/redeyed": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz",
+ "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "~4.0.0"
+ }
+ },
+ "node_modules/redis": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-5.11.0.tgz",
+ "integrity": "sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@redis/bloom": "5.11.0",
+ "@redis/client": "5.11.0",
+ "@redis/json": "5.11.0",
+ "@redis/search": "5.11.0",
+ "@redis/time-series": "5.11.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
+ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.2",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.13.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/registry-auth-token": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz",
+ "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+ "dev": true,
+ "dependencies": {
+ "@pnpm/npm-conf": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
+ "dev": true
+ },
+ "node_modules/regjsparser": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz",
+ "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==",
+ "dev": true,
+ "dependencies": {
+ "jsesc": "~3.1.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/rehackt": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
+ "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
+ "dev": true,
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "es6-error": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/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==",
+ "dev": true
+ },
+ "node_modules/requirejs": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz",
+ "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==",
+ "dev": true,
+ "bin": {
+ "r_js": "bin/r.js",
+ "r.js": "bin/r.js"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/requirejs-config-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz",
+ "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.0",
+ "stringify-object": "^3.2.1"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/requizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
+ "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.21"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve-dependency-path": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-4.0.0.tgz",
+ "integrity": "sha512-hlY1SybBGm5aYN3PC4rp15MzsJLM1w+MEA/4KU3UBPfz4S0lL3FL6mgv7JgaA8a+ZTeEQAiF1a1BuN2nkqiIlg==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/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,
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/retry-request": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+ "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/request": "^2.48.8",
+ "extend": "^3.0.2",
+ "teeny-request": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/router/node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
+ },
+ "node_modules/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,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz",
+ "integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/sass-lookup": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-6.0.1.tgz",
+ "integrity": "sha512-nl9Wxbj9RjEJA5SSV0hSDoU2zYGtE+ANaDS4OFUR7nYrquvBFvPKZZtQHe3lvnxCcylEDV00KUijjdMTUElcVQ==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^12.0.0"
+ },
+ "bin": {
+ "sass-lookup": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/sass-lookup/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/seek-bzip": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz",
+ "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^2.8.1"
+ },
+ "bin": {
+ "seek-bunzip": "bin/seek-bunzip",
+ "seek-table": "bin/seek-bzip-table"
+ }
+ },
+ "node_modules/seek-bzip/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release": {
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.3.tgz",
+ "integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@semantic-release/commit-analyzer": "^13.0.1",
+ "@semantic-release/error": "^4.0.0",
+ "@semantic-release/github": "^12.0.0",
+ "@semantic-release/npm": "^13.1.1",
+ "@semantic-release/release-notes-generator": "^14.1.0",
+ "aggregate-error": "^5.0.0",
+ "cosmiconfig": "^9.0.0",
+ "debug": "^4.0.0",
+ "env-ci": "^11.0.0",
+ "execa": "^9.0.0",
+ "figures": "^6.0.0",
+ "find-versions": "^6.0.0",
+ "get-stream": "^6.0.0",
+ "git-log-parser": "^1.2.0",
+ "hook-std": "^4.0.0",
+ "hosted-git-info": "^9.0.0",
+ "import-from-esm": "^2.0.0",
+ "lodash-es": "^4.17.21",
+ "marked": "^15.0.0",
+ "marked-terminal": "^7.3.0",
+ "micromatch": "^4.0.2",
+ "p-each-series": "^3.0.0",
+ "p-reduce": "^3.0.0",
+ "read-package-up": "^12.0.0",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.3.2",
+ "signale": "^1.2.1",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "semantic-release": "bin/semantic-release.js"
+ },
+ "engines": {
+ "node": "^22.14.0 || >= 24.10.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/@semantic-release/npm": {
+ "version": "13.1.5",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-13.1.5.tgz",
+ "integrity": "sha512-Hq5UxzoatN3LHiq2rTsWS54nCdqJHlsssGERCo8WlvdfFA9LoN0vO+OuKVSjtNapIc/S8C2LBj206wKLHg62mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@actions/core": "^3.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "env-ci": "^11.2.0",
+ "execa": "^9.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^9.0.0",
+ "npm": "^11.6.2",
+ "rc": "^1.2.8",
+ "read-pkg": "^10.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ },
+ "engines": {
+ "node": "^22.14.0 || >= 24.10.0"
+ },
+ "peerDependencies": {
+ "semantic-release": ">=20.1.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "5.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/semantic-release/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/execa": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
+ "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
+ "dev": true,
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "cross-spawn": "^7.0.3",
+ "figures": "^6.1.0",
+ "get-stream": "^9.0.0",
+ "human-signals": "^7.0.0",
+ "is-plain-obj": "^4.1.0",
+ "is-stream": "^4.0.1",
+ "npm-run-path": "^5.2.0",
+ "pretty-ms": "^9.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^4.0.0",
+ "yoctocolors": "^2.0.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.5.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "dev": true,
+ "dependencies": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/hook-std": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-4.0.0.tgz",
+ "integrity": "sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/hosted-git-info": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz",
+ "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^11.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/human-signals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
+ "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.20"
+ }
+ },
+ "node_modules/semantic-release/node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/index-to-position": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz",
+ "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/marked": {
+ "version": "15.0.12",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
+ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/normalize-package-data": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz",
+ "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^9.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/normalize-url": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-9.0.0.tgz",
+ "integrity": "sha512-z9nC87iaZXXySbWWtTHfCFJyFvKaUAW6lODhikG7ILSbVgmwuFjUqkgnheHvAUcGedO29e2QGBRXMUD64aurqQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-11.12.0.tgz",
+ "integrity": "sha512-xPhOap4ZbJWyd7DAOukP564WFwNSGu/2FeTRFHhiiKthcauxhH/NpkJAQm24xD+cAn8av5tQ00phi98DqtfLsg==",
+ "bundleDependencies": [
+ "@isaacs/string-locale-compare",
+ "@npmcli/arborist",
+ "@npmcli/config",
+ "@npmcli/fs",
+ "@npmcli/map-workspaces",
+ "@npmcli/metavuln-calculator",
+ "@npmcli/package-json",
+ "@npmcli/promise-spawn",
+ "@npmcli/redact",
+ "@npmcli/run-script",
+ "@sigstore/tuf",
+ "abbrev",
+ "archy",
+ "cacache",
+ "chalk",
+ "ci-info",
+ "fastest-levenshtein",
+ "fs-minipass",
+ "glob",
+ "graceful-fs",
+ "hosted-git-info",
+ "ini",
+ "init-package-json",
+ "is-cidr",
+ "json-parse-even-better-errors",
+ "libnpmaccess",
+ "libnpmdiff",
+ "libnpmexec",
+ "libnpmfund",
+ "libnpmorg",
+ "libnpmpack",
+ "libnpmpublish",
+ "libnpmsearch",
+ "libnpmteam",
+ "libnpmversion",
+ "make-fetch-happen",
+ "minimatch",
+ "minipass",
+ "minipass-pipeline",
+ "ms",
+ "node-gyp",
+ "nopt",
+ "npm-audit-report",
+ "npm-install-checks",
+ "npm-package-arg",
+ "npm-pick-manifest",
+ "npm-profile",
+ "npm-registry-fetch",
+ "npm-user-validate",
+ "p-map",
+ "pacote",
+ "parse-conflict-json",
+ "proc-log",
+ "qrcode-terminal",
+ "read",
+ "semver",
+ "spdx-expression-parse",
+ "ssri",
+ "supports-color",
+ "tar",
+ "text-table",
+ "tiny-relative-date",
+ "treeverse",
+ "validate-npm-package-name",
+ "which"
+ ],
+ "dev": true,
+ "license": "Artistic-2.0",
+ "workspaces": [
+ "docs",
+ "smoke-tests",
+ "mock-globals",
+ "mock-registry",
+ "workspaces/*"
+ ],
+ "dependencies": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/config": "^10.8.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/map-workspaces": "^5.0.3",
+ "@npmcli/metavuln-calculator": "^9.0.3",
+ "@npmcli/package-json": "^7.0.5",
+ "@npmcli/promise-spawn": "^9.0.1",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.4",
+ "@sigstore/tuf": "^4.0.2",
+ "abbrev": "^4.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^20.0.4",
+ "chalk": "^5.6.2",
+ "ci-info": "^4.4.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^13.0.6",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^9.0.2",
+ "ini": "^6.0.0",
+ "init-package-json": "^8.2.5",
+ "is-cidr": "^6.0.3",
+ "json-parse-even-better-errors": "^5.0.0",
+ "libnpmaccess": "^10.0.3",
+ "libnpmdiff": "^8.1.5",
+ "libnpmexec": "^10.2.5",
+ "libnpmfund": "^7.0.19",
+ "libnpmorg": "^8.0.1",
+ "libnpmpack": "^9.1.5",
+ "libnpmpublish": "^11.1.3",
+ "libnpmsearch": "^9.0.1",
+ "libnpmteam": "^8.0.2",
+ "libnpmversion": "^8.0.3",
+ "make-fetch-happen": "^15.0.5",
+ "minimatch": "^10.2.4",
+ "minipass": "^7.1.3",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^12.2.0",
+ "nopt": "^9.0.0",
+ "npm-audit-report": "^7.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.2",
+ "npm-pick-manifest": "^11.0.3",
+ "npm-profile": "^12.0.1",
+ "npm-registry-fetch": "^19.1.1",
+ "npm-user-validate": "^4.0.0",
+ "p-map": "^7.0.4",
+ "pacote": "^21.5.0",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.1.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^5.0.1",
+ "semver": "^7.7.4",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^13.0.1",
+ "supports-color": "^10.2.2",
+ "tar": "^7.5.11",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^2.0.2",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^7.0.2",
+ "which": "^6.0.1"
+ },
+ "bin": {
+ "npm": "bin/npm-cli.js",
+ "npx": "bin/npx-cli.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@gar/promise-retry": {
+ "version": "1.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/agent": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^11.2.1",
+ "socks-proxy-agent": "^8.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/arborist": {
+ "version": "9.4.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/metavuln-calculator": "^9.0.2",
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/query": "^5.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "bin-links": "^6.0.0",
+ "cacache": "^20.0.1",
+ "common-ancestor-path": "^2.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^11.2.1",
+ "minimatch": "^10.0.3",
+ "nopt": "^9.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "pacote": "^21.0.2",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.0.0",
+ "proggy": "^4.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "semver": "^7.3.7",
+ "ssri": "^13.0.0",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^4.0.0"
+ },
+ "bin": {
+ "arborist": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/config": {
+ "version": "10.8.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "ini": "^6.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "walk-up-path": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/fs": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/git": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "ini": "^6.0.0",
+ "lru-cache": "^11.2.1",
+ "npm-pick-manifest": "^11.0.1",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "which": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/installed-package-contents": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-bundled": "^5.0.0",
+ "npm-normalize-package-bin": "^5.0.0"
+ },
+ "bin": {
+ "installed-package-contents": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/map-workspaces": {
+ "version": "5.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "glob": "^13.0.0",
+ "minimatch": "^10.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
+ "version": "9.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cacache": "^20.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "pacote": "^21.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/name-from-folder": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/node-gyp": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/package-json": {
+ "version": "7.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^7.0.0",
+ "glob": "^13.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.5.3",
+ "spdx-expression-parse": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/promise-spawn": {
+ "version": "9.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "which": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/query": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/redact": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@npmcli/run-script": {
+ "version": "10.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "node-gyp": "^12.1.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/bundle": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.5.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/core": {
+ "version": "3.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/protobuf-specs": {
+ "version": "0.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/sign": {
+ "version": "4.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.2",
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.2.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "make-fetch-happen": "^15.0.4",
+ "proc-log": "^6.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/tuf": {
+ "version": "4.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "tuf-js": "^4.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@sigstore/verify": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/@tufjs/models": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^10.1.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/abbrev": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/agent-base": {
+ "version": "7.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/aproba": {
+ "version": "2.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/archy": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/bin-links": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "cmd-shim": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "read-cmd-shim": "^6.0.0",
+ "write-file-atomic": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/binary-extensions": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/cacache": {
+ "version": "20.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^5.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^13.0.0",
+ "lru-cache": "^11.1.0",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^7.0.2",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/chalk": {
+ "version": "5.6.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/chownr": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ci-info": {
+ "version": "4.4.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/cidr-regex": {
+ "version": "5.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/cmd-shim": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/common-ancestor-path": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/debug": {
+ "version": "4.4.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/diff": {
+ "version": "8.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/env-paths": {
+ "version": "2.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/exponential-backoff": {
+ "version": "3.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/fs-minipass": {
+ "version": "3.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/glob": {
+ "version": "13.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/hosted-git-info": {
+ "version": "9.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^11.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ignore-walk": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minimatch": "^10.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ini": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/init-package-json": {
+ "version": "8.2.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/package-json": "^7.0.0",
+ "npm-package-arg": "^13.0.0",
+ "promzard": "^3.0.1",
+ "read": "^5.0.1",
+ "semver": "^7.7.2",
+ "validate-npm-package-name": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ip-address": {
+ "version": "10.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/is-cidr": {
+ "version": "6.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "cidr-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/isexe": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/json-parse-even-better-errors": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/json-stringify-nice": {
+ "version": "1.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/jsonparse": {
+ "version": "1.3.1",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/just-diff": {
+ "version": "6.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/just-diff-apply": {
+ "version": "5.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmaccess": {
+ "version": "10.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmdiff": {
+ "version": "8.1.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "binary-extensions": "^3.0.0",
+ "diff": "^8.0.2",
+ "minimatch": "^10.0.3",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "tar": "^7.5.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmexec": {
+ "version": "10.2.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "proc-log": "^6.0.0",
+ "read": "^5.0.1",
+ "semver": "^7.3.7",
+ "signal-exit": "^4.1.0",
+ "walk-up-path": "^4.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmfund": {
+ "version": "7.0.19",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmorg": {
+ "version": "8.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpack": {
+ "version": "9.1.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/run-script": "^10.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmpublish": {
+ "version": "11.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmsearch": {
+ "version": "9.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmteam": {
+ "version": "8.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/libnpmversion": {
+ "version": "8.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/lru-cache": {
+ "version": "11.2.7",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/make-fetch-happen": {
+ "version": "15.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/agent": "^4.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "cacache": "^20.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^1.0.0",
+ "proc-log": "^6.0.0",
+ "ssri": "^13.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minimatch": {
+ "version": "10.2.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass": {
+ "version": "7.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-collect": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-fetch": {
+ "version": "5.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.0.3",
+ "minipass-sized": "^2.0.0",
+ "minizlib": "^3.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ },
+ "optionalDependencies": {
+ "iconv-lite": "^0.7.2"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-flush/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": {
+ "version": "3.3.6",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-pipeline/node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minipass-sized": {
+ "version": "2.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/minizlib": {
+ "version": "3.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/mute-stream": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/negotiator": {
+ "version": "1.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/node-gyp": {
+ "version": "12.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^15.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.5.4",
+ "tinyglobby": "^0.2.12",
+ "which": "^6.0.0"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/nopt": {
+ "version": "9.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^4.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-audit-report": {
+ "version": "7.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-bundled": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-normalize-package-bin": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-install-checks": {
+ "version": "8.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-normalize-package-bin": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-package-arg": {
+ "version": "13.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^7.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-packlist": {
+ "version": "10.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "ignore-walk": "^8.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-pick-manifest": {
+ "version": "11.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-install-checks": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "npm-package-arg": "^13.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-profile": {
+ "version": "12.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-registry-fetch": {
+ "version": "19.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/redact": "^4.0.0",
+ "jsonparse": "^1.3.1",
+ "make-fetch-happen": "^15.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minizlib": "^3.0.1",
+ "npm-package-arg": "^13.0.0",
+ "proc-log": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/npm-user-validate": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/p-map": {
+ "version": "7.0.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/pacote": {
+ "version": "21.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "cacache": "^20.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^13.0.0",
+ "npm-packlist": "^10.0.1",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0",
+ "tar": "^7.4.3"
+ },
+ "bin": {
+ "pacote": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/parse-conflict-json": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^5.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/path-scurry": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/postcss-selector-parser": {
+ "version": "7.1.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/proc-log": {
+ "version": "6.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/proggy": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/promise-all-reject-late": {
+ "version": "1.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/promise-call-limit": {
+ "version": "3.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/promzard": {
+ "version": "3.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "read": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/qrcode-terminal": {
+ "version": "0.12.0",
+ "dev": true,
+ "inBundle": true,
+ "bin": {
+ "qrcode-terminal": "bin/qrcode-terminal.js"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/read": {
+ "version": "5.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "mute-stream": "^3.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/read-cmd-shim": {
+ "version": "6.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/semver": {
+ "version": "7.7.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/sigstore": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "@sigstore/sign": "^4.1.0",
+ "@sigstore/tuf": "^4.0.1",
+ "@sigstore/verify": "^3.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/socks": {
+ "version": "2.8.7",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/spdx-exceptions": {
+ "version": "2.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/spdx-expression-parse": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/spdx-license-ids": {
+ "version": "3.0.23",
+ "dev": true,
+ "inBundle": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/ssri": {
+ "version": "13.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/supports-color": {
+ "version": "10.2.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tar": {
+ "version": "7.5.11",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tiny-relative-date": {
+ "version": "2.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/treeverse": {
+ "version": "3.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/tuf-js": {
+ "version": "4.1.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/models": "4.1.0",
+ "debug": "^4.4.3",
+ "make-fetch-happen": "^15.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/validate-npm-package-name": {
+ "version": "7.0.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/walk-up-path": {
+ "version": "4.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/which": {
+ "version": "6.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^4.0.0"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/write-file-atomic": {
+ "version": "7.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/semantic-release/node_modules/npm/node_modules/yallist": {
+ "version": "5.0.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/semantic-release/node_modules/p-reduce": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
+ "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/parse-json": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
+ "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "index-to-position": "^1.1.0",
+ "type-fest": "^4.39.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/parse-json/node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/pretty-ms": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
+ "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
+ "dev": true,
+ "dependencies": {
+ "parse-ms": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/read-package-up": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz",
+ "integrity": "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up-simple": "^1.0.1",
+ "read-pkg": "^10.0.0",
+ "type-fest": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/read-pkg": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz",
+ "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/normalize-package-data": "^2.4.4",
+ "normalize-package-data": "^8.0.0",
+ "parse-json": "^8.3.0",
+ "type-fest": "^5.4.4",
+ "unicorn-magic": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semantic-release/node_modules/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,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/semantic-release/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/strip-final-newline": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+ "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/type-fest": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz",
+ "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "dependencies": {
+ "tagged-tag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/unicorn-magic": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+ "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semantic-release/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/semantic-release/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semantic-release/node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/semantic-release/node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
+ "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/semver-regex": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz",
+ "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/send/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/send/node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/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=="
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/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==",
+ "devOptional": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/showdown": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
+ "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^9.0.0"
+ },
+ "bin": {
+ "showdown": "bin/showdown.js"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://www.paypal.me/tiviesantos"
+ }
+ },
+ "node_modules/showdown/node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/signale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz",
+ "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^2.3.2",
+ "figures": "^2.0.0",
+ "pkg-conf": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/signale/node_modules/figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/skin-tone": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
+ "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
+ "dev": true,
+ "dependencies": {
+ "unicode-emoji-modifier-base": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+ "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.3",
+ "is-fullwidth-code-point": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sparse-bitfield": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+ "dependencies": {
+ "memory-pager": "^1.0.2"
+ }
+ },
+ "node_modules/spawn-error-forwarder": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz",
+ "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==",
+ "dev": true
+ },
+ "node_modules/spawn-wrap": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
+ "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
+ "dev": true,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/spawn-wrap/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/spawn-wrap/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "dev": true,
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "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": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz",
+ "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==",
+ "dev": true
+ },
+ "node_modules/spex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/spex/-/spex-4.1.0.tgz",
+ "integrity": "sha512-ktgNAQ1X9x1A3IMChM6XBDeVjhGPbLgPQ8aEzGOaUIhZTnLeJSBApvi3gXT789hee6h73N3jOeWkXDwoPbYT/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/stream-combiner2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==",
+ "dev": true,
+ "dependencies": {
+ "duplexer2": "~0.1.0",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/stream-to-array": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz",
+ "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==",
+ "dev": true,
+ "dependencies": {
+ "any-promise": "^1.1.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/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==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/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=="
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/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==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "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==",
+ "devOptional": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "dev": true,
+ "dependencies": {
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/stringify-object/node_modules/is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "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,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-dirs": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz",
+ "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-natural-number": "^4.0.1"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz",
+ "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/stylus-lookup": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-6.0.0.tgz",
+ "integrity": "sha512-RaWKxAvPnIXrdby+UWCr1WRfa+lrPMSJPySte4Q6a+rWyjeJyFOLJxr5GrAVfcMCsfVlCuzTAJ/ysYT8p8do7Q==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^12.0.0"
+ },
+ "bin": {
+ "stylus-lookup": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/stylus-lookup/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/subscriptions-transport-ws": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz",
+ "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==",
+ "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "backo2": "^1.0.2",
+ "eventemitter3": "^3.1.0",
+ "iterall": "^1.2.1",
+ "symbol-observable": "^1.0.4",
+ "ws": "^5.2.0 || ^6.0.0 || ^7.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "^15.7.2 || ^16.0.0"
+ }
+ },
+ "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/subscriptions-transport-ws/node_modules/ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/super-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz",
+ "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==",
+ "dev": true,
+ "dependencies": {
+ "function-timeout": "^1.0.1",
+ "time-span": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
+ "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
+ }
+ },
+ "node_modules/supports-hyperlinks/node_modules/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,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks/node_modules/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,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/tagged-tag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
+ "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^1.0.0",
+ "buffer-alloc": "^1.2.0",
+ "end-of-stream": "^1.0.0",
+ "fs-constants": "^1.0.0",
+ "readable-stream": "^2.3.0",
+ "to-buffer": "^1.1.1",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/tar-stream/node_modules/bl": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
+ "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.3.5",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/teeny-request": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
+ "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.9",
+ "stream-events": "^1.0.5",
+ "uuid": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/teeny-request/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/teeny-request/node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/teeny-request/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/teeny-request/node_modules/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==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/teeny-request/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/teeny-request/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/teeny-request/node_modules/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==",
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/teeny-request/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/temp-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz",
+ "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/tempy": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz",
+ "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==",
+ "dev": true,
+ "dependencies": {
+ "is-stream": "^3.0.0",
+ "temp-dir": "^3.0.0",
+ "type-fest": "^2.12.2",
+ "unique-string": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tempy/node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tempy/node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.0.tgz",
+ "integrity": "sha512-Y/SblUl5kEyEFzhMAQdsxVHh+utAxd4IuRNJzKywY/4uzSogh3G219jqbDDxYu4MXO9CzY3tSEqmZvW6AoEDJw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/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,
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-extensions": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+ "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/time-span": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz",
+ "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==",
+ "dev": true,
+ "dependencies": {
+ "convert-hrtime": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tinyexec": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz",
+ "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
+ "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/to-buffer/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ },
+ "node_modules/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,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/traverse": {
+ "version": "0.6.7",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz",
+ "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/ts-graphviz": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-2.1.4.tgz",
+ "integrity": "sha512-0g465/ES70H0h5rcLUqaenKqNYekQaR9W0m0xUGy3FxueGujpGr+0GN2YWlgLIYSE2Xg0W7Uq1Qqnn7Cg+Af2w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ts-graphviz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ts-graphviz"
+ }
+ ],
+ "dependencies": {
+ "@ts-graphviz/adapter": "^2.0.5",
+ "@ts-graphviz/ast": "^2.0.5",
+ "@ts-graphviz/common": "^2.1.4",
+ "@ts-graphviz/core": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/ts-invariant": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
+ "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+ "dev": true,
+ "dependencies": {
+ "json5": "^2.2.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
+ }
+ },
+ "node_modules/tv4": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+ "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz",
+ "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
+ "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.58.0",
+ "@typescript-eslint/parser": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "dev": true
+ },
+ "node_modules/uglify-js": {
+ "version": "3.18.0",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz",
+ "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ }
+ },
+ "node_modules/underscore": {
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
+ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
+ "dev": true
+ },
+ "node_modules/undici": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
+ "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-emoji-modifier-base": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
+ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
+ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unique-string": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
+ "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
+ "dev": true,
+ "dependencies": {
+ "crypto-random-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/universal-user-agent": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
+ "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url-join": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
+ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vasync": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
+ "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/vasync/node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
+ },
+ "node_modules/vasync/node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
+ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/verror/node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
+ },
+ "node_modules/walkdir": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+ "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/web-push": {
+ "version": "3.6.7",
+ "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz",
+ "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==",
+ "dependencies": {
+ "asn1.js": "^5.3.0",
+ "http_ece": "1.2.0",
+ "https-proxy-agent": "^7.0.0",
+ "jws": "^4.0.0",
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "web-push": "src/cli.js"
+ },
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
+ "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dependencies": {
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "devOptional": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/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==",
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "node_modules/winston": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
+ "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
+ "dependencies": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.8",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.7.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-daily-rotate-file": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz",
+ "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==",
+ "dependencies": {
+ "file-stream-rotator": "^0.6.1",
+ "object-hash": "^3.0.0",
+ "triple-beam": "^1.4.1",
+ "winston-transport": "^4.7.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "winston": "^3"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "dependencies": {
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/winston-transport/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/winston/node_modules/@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/winston/node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "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/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true
+ },
+ "node_modules/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,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "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==",
+ "devOptional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/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,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/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,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/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
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/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==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/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==",
+ "dev": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/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,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xmlcreate": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
+ "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
+ "dev": true
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/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
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/yaml": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/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,
+ "dependencies": {
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/node_modules/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,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/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,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/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,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yargs/node_modules/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,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/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,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/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==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yoctocolors": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
+ "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zen-observable": {
+ "version": "0.8.15",
+ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
+ "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
+ "dev": true
+ },
+ "node_modules/zen-observable-ts": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
+ "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
+ "dev": true,
+ "dependencies": {
+ "zen-observable": "0.8.15"
+ }
+ },
+ "spec/dependencies/mock-files-adapter": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "spec/dependencies/mock-mail-adapter": {
+ "version": "1.0.0",
+ "dev": true
+ }
+ },
+ "dependencies": {
+ "@actions/core": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.0.tgz",
+ "integrity": "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==",
+ "dev": true,
+ "requires": {
+ "@actions/exec": "^3.0.0",
+ "@actions/http-client": "^4.0.0"
+ }
+ },
+ "@actions/exec": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
+ "integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
+ "dev": true,
+ "requires": {
+ "@actions/io": "^3.0.2"
+ }
+ },
+ "@actions/http-client": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
+ "integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
+ "dev": true,
+ "requires": {
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
+ }
+ },
+ "@actions/io": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
+ "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
+ "dev": true
+ },
+ "@apollo/cache-control-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz",
+ "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==",
+ "requires": {}
+ },
+ "@apollo/client": {
+ "version": "3.13.8",
+ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz",
+ "integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==",
+ "dev": true,
+ "requires": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@wry/caches": "^1.0.0",
+ "@wry/equality": "^0.5.6",
+ "@wry/trie": "^0.5.0",
+ "graphql-tag": "^2.12.6",
+ "hoist-non-react-statics": "^3.3.2",
+ "optimism": "^0.18.0",
+ "prop-types": "^15.7.2",
+ "rehackt": "^0.1.0",
+ "symbol-observable": "^4.0.0",
+ "ts-invariant": "^0.10.3",
+ "tslib": "^2.3.0",
+ "zen-observable-ts": "^1.2.5"
+ }
+ },
+ "@apollo/protobufjs": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz",
+ "integrity": "sha512-Lahx5zntHPZia35myYDBRuF58tlwPskwHc5CWBZC/4bMKB6siTBWwtMrkqXcsNwQiFSzSx5hKdRPUmemrEp3Gg==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.0",
+ "long": "^4.0.0"
+ }
+ },
+ "@apollo/server": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz",
+ "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==",
+ "requires": {
+ "@apollo/cache-control-types": "^1.0.3",
+ "@apollo/server-gateway-interface": "^2.0.0",
+ "@apollo/usage-reporting-protobuf": "^4.1.1",
+ "@apollo/utils.createhash": "^3.0.0",
+ "@apollo/utils.fetcher": "^3.0.0",
+ "@apollo/utils.isnodelike": "^3.0.0",
+ "@apollo/utils.keyvaluecache": "^4.0.0",
+ "@apollo/utils.logger": "^3.0.0",
+ "@apollo/utils.usagereporting": "^2.1.0",
+ "@apollo/utils.withrequired": "^3.0.0",
+ "@graphql-tools/schema": "^10.0.0",
+ "async-retry": "^1.2.1",
+ "body-parser": "^2.2.2",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "finalhandler": "^2.1.0",
+ "loglevel": "^1.6.8",
+ "lru-cache": "^11.1.0",
+ "negotiator": "^1.0.0",
+ "uuid": "^11.1.0",
+ "whatwg-mimetype": "^4.0.0"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="
+ }
+ }
+ },
+ "@apollo/server-gateway-interface": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz",
+ "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==",
+ "requires": {
+ "@apollo/usage-reporting-protobuf": "^4.1.1",
+ "@apollo/utils.fetcher": "^3.0.0",
+ "@apollo/utils.keyvaluecache": "^4.0.0",
+ "@apollo/utils.logger": "^3.0.0"
+ }
+ },
+ "@apollo/usage-reporting-protobuf": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz",
+ "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==",
+ "requires": {
+ "@apollo/protobufjs": "1.2.7"
+ }
+ },
+ "@apollo/utils.createhash": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz",
+ "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==",
+ "requires": {
+ "@apollo/utils.isnodelike": "^3.0.0",
+ "sha.js": "^2.4.11"
+ }
+ },
+ "@apollo/utils.dropunuseddefinitions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz",
+ "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==",
+ "requires": {}
+ },
+ "@apollo/utils.fetcher": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz",
+ "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A=="
+ },
+ "@apollo/utils.isnodelike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz",
+ "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g=="
+ },
+ "@apollo/utils.keyvaluecache": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz",
+ "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==",
+ "requires": {
+ "@apollo/utils.logger": "^3.0.0",
+ "lru-cache": "^11.0.0"
+ }
+ },
+ "@apollo/utils.logger": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz",
+ "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg=="
+ },
+ "@apollo/utils.printwithreducedwhitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz",
+ "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==",
+ "requires": {}
+ },
+ "@apollo/utils.removealiases": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz",
+ "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==",
+ "requires": {}
+ },
+ "@apollo/utils.sortast": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz",
+ "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==",
+ "requires": {
+ "lodash.sortby": "^4.7.0"
+ }
+ },
+ "@apollo/utils.stripsensitiveliterals": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz",
+ "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==",
+ "requires": {}
+ },
+ "@apollo/utils.usagereporting": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz",
+ "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==",
+ "requires": {
+ "@apollo/usage-reporting-protobuf": "^4.1.0",
+ "@apollo/utils.dropunuseddefinitions": "^2.0.1",
+ "@apollo/utils.printwithreducedwhitespace": "^2.0.1",
+ "@apollo/utils.removealiases": "2.0.1",
+ "@apollo/utils.sortast": "^2.0.1",
+ "@apollo/utils.stripsensitiveliterals": "^2.0.1"
+ }
+ },
+ "@apollo/utils.withrequired": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz",
+ "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA=="
+ },
+ "@as-integrations/express5": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@as-integrations/express5/-/express5-1.1.2.tgz",
+ "integrity": "sha512-BxfwtcWNf2CELDkuPQxi5Zl3WqY/dQVJYafeCBOGoFQjv5M0fjhxmAFZ9vKx/5YKKNeok4UY6PkFbHzmQrdxIA==",
+ "requires": {}
+ },
+ "@babel/cli": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz",
+ "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+ "chokidar": "^3.6.0",
+ "commander": "^6.2.0",
+ "convert-source-map": "^2.0.0",
+ "fs-readdir-recursive": "^1.1.0",
+ "glob": "^7.2.0",
+ "make-dir": "^2.1.0",
+ "slash": "^2.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true
+ },
+ "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
+ }
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
+ "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/eslint-parser": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz",
+ "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==",
+ "dev": true,
+ "requires": {
+ "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+ "eslint-visitor-keys": "^2.1.0",
+ "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/generator": {
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.27.3"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
+ "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/traverse": "^7.28.6",
+ "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-create-regexp-features-plugin": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "regexpu-core": "^6.3.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-define-polyfill-provider": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
+ "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "debug": "^4.4.3",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.11"
+ }
+ },
+ "@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
+ "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.27.1"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz",
+ "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-wrap-function": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
+ "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/traverse": "^7.28.6"
+ }
+ },
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
+ "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz",
+ "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.29.0"
+ }
+ },
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
+ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ }
+ },
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
+ "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
+ "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
+ "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.27.1"
+ }
+ },
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
+ "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/traverse": "^7.28.6"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz",
+ "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.7",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.20.7"
+ }
+ },
+ "@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "requires": {}
+ },
+ "@babel/plugin-syntax-flow": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz",
+ "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-syntax-import-assertions": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
+ "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-syntax-import-attributes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
+ "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
+ "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
+ "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
+ "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-async-generator-functions": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz",
+ "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-remap-async-to-generator": "^7.27.1",
+ "@babel/traverse": "^7.29.0"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz",
+ "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-remap-async-to-generator": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
+ "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz",
+ "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-class-properties": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz",
+ "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-class-static-block": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
+ "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz",
+ "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-replace-supers": "^7.28.6",
+ "@babel/traverse": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz",
+ "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/template": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
+ "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
+ "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
+ "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz",
+ "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-dynamic-import": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
+ "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
+ "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
+ "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-export-namespace-from": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
+ "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-flow-strip-types": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz",
+ "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-flow": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
+ "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
+ "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-json-strings": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
+ "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
+ "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz",
+ "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
+ "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
+ "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz",
+ "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
+ "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.29.0"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
+ "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz",
+ "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
+ "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz",
+ "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-numeric-separator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz",
+ "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-object-rest-spread": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
+ "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/traverse": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
+ "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz",
+ "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-optional-chaining": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz",
+ "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.27.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz",
+ "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-private-methods": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz",
+ "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-private-property-in-object": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz",
+ "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
+ "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz",
+ "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
+ "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
+ "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
+ "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz",
+ "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz",
+ "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
+ "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
+ "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz",
+ "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-syntax-typescript": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-unicode-escapes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
+ "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
+ "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
+ "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ }
+ },
+ "@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
+ "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.28.5",
+ "@babel/helper-plugin-utils": "^7.28.6"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz",
+ "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.28.6",
+ "@babel/plugin-syntax-import-attributes": "^7.28.6",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.27.1",
+ "@babel/plugin-transform-async-generator-functions": "^7.29.0",
+ "@babel/plugin-transform-async-to-generator": "^7.28.6",
+ "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
+ "@babel/plugin-transform-block-scoping": "^7.28.6",
+ "@babel/plugin-transform-class-properties": "^7.28.6",
+ "@babel/plugin-transform-class-static-block": "^7.28.6",
+ "@babel/plugin-transform-classes": "^7.28.6",
+ "@babel/plugin-transform-computed-properties": "^7.28.6",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-dotall-regex": "^7.28.6",
+ "@babel/plugin-transform-duplicate-keys": "^7.27.1",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-dynamic-import": "^7.27.1",
+ "@babel/plugin-transform-explicit-resource-management": "^7.28.6",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.6",
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
+ "@babel/plugin-transform-for-of": "^7.27.1",
+ "@babel/plugin-transform-function-name": "^7.27.1",
+ "@babel/plugin-transform-json-strings": "^7.28.6",
+ "@babel/plugin-transform-literals": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.6",
+ "@babel/plugin-transform-member-expression-literals": "^7.27.1",
+ "@babel/plugin-transform-modules-amd": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.28.6",
+ "@babel/plugin-transform-modules-systemjs": "^7.29.0",
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
+ "@babel/plugin-transform-new-target": "^7.27.1",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6",
+ "@babel/plugin-transform-numeric-separator": "^7.28.6",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.6",
+ "@babel/plugin-transform-object-super": "^7.27.1",
+ "@babel/plugin-transform-optional-catch-binding": "^7.28.6",
+ "@babel/plugin-transform-optional-chaining": "^7.28.6",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/plugin-transform-private-methods": "^7.28.6",
+ "@babel/plugin-transform-private-property-in-object": "^7.28.6",
+ "@babel/plugin-transform-property-literals": "^7.27.1",
+ "@babel/plugin-transform-regenerator": "^7.29.0",
+ "@babel/plugin-transform-regexp-modifiers": "^7.28.6",
+ "@babel/plugin-transform-reserved-words": "^7.27.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.27.1",
+ "@babel/plugin-transform-spread": "^7.28.6",
+ "@babel/plugin-transform-sticky-regex": "^7.27.1",
+ "@babel/plugin-transform-template-literals": "^7.27.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.27.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.27.1",
+ "@babel/plugin-transform-unicode-property-regex": "^7.28.6",
+ "@babel/plugin-transform-unicode-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.28.6",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.15",
+ "babel-plugin-polyfill-corejs3": "^0.14.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.6",
+ "core-js-compat": "^3.48.0",
+ "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/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ }
+ },
+ "@babel/preset-typescript": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
+ "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-syntax-jsx": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.27.1",
+ "@babel/plugin-transform-typescript": "^7.27.1"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="
+ },
+ "@babel/runtime-corejs3": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz",
+ "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==",
+ "requires": {
+ "core-js-pure": "^3.48.0"
+ }
+ },
+ "@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ }
+ },
+ "@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ }
+ },
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@dabh/diagnostics": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
+ "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
+ "requires": {
+ "@so-ric/colorspace": "^1.1.6",
+ "enabled": "2.0.x",
+ "kuler": "^2.0.0"
+ }
+ },
+ "@dependents/detective-less": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.0.tgz",
+ "integrity": "sha512-D/9dozteKcutI5OdxJd8rU+fL6XgaaRg60sPPJWkT33OCiRfkCu5wO5B/yXTaaL2e6EB0lcCBGe5E0XscZCvvQ==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "@emnapi/core": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz",
+ "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==",
+ "optional": true,
+ "requires": {
+ "@emnapi/wasi-threads": "1.0.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "@emnapi/runtime": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
+ "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+ "optional": true,
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "@emnapi/wasi-threads": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz",
+ "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==",
+ "optional": true,
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true
+ },
+ "@eslint/config-array": {
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
+ "dev": true,
+ "requires": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ }
+ },
+ "@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
+ "dev": true
+ },
+ "@eslint/core": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
+ "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.15"
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.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"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true
+ }
+ }
+ },
+ "@eslint/js": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
+ "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
+ "dev": true
+ },
+ "@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true
+ },
+ "@eslint/plugin-kit": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
+ "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
+ "dev": true,
+ "requires": {
+ "@eslint/core": "^0.15.1",
+ "levn": "^0.4.1"
+ },
+ "dependencies": {
+ "@eslint/core": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.15"
+ }
+ }
+ }
+ },
+ "@fastify/busboy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+ "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA=="
+ },
+ "@firebase/app-check-interop-types": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz",
+ "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A=="
+ },
+ "@firebase/app-types": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
+ "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw=="
+ },
+ "@firebase/auth-interop-types": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz",
+ "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA=="
+ },
+ "@firebase/component": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.2.tgz",
+ "integrity": "sha512-iyVDGc6Vjx7Rm0cAdccLH/NG6fADsgJak/XW9IA2lPf8AjIlsemOpFGKczYyPHxm4rnKdR8z6sK4+KEC7NwmEg==",
+ "requires": {
+ "@firebase/util": "1.15.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.2.tgz",
+ "integrity": "sha512-lP96CMjMPy/+d1d9qaaHjHHdzdwvEOuyyLq9ehX89e2XMKwS1jHNzYBO+42bdSumuj5ukPbmnFtViZu8YOMT+w==",
+ "requires": {
+ "@firebase/app-check-interop-types": "0.3.3",
+ "@firebase/auth-interop-types": "0.2.4",
+ "@firebase/component": "0.7.2",
+ "@firebase/logger": "0.5.0",
+ "@firebase/util": "1.15.0",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database-compat": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.2.tgz",
+ "integrity": "sha512-j4A6IhVZbgxAzT6gJJC2PfOxYCK9SrDrUO7nTM4EscTYtKkAkzsbKoCnDdjFapQfnsncvPWjqVTr/0PffUwg3g==",
+ "requires": {
+ "@firebase/component": "0.7.2",
+ "@firebase/database": "1.1.2",
+ "@firebase/database-types": "1.0.18",
+ "@firebase/logger": "0.5.0",
+ "@firebase/util": "1.15.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/database-types": {
+ "version": "1.0.18",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.18.tgz",
+ "integrity": "sha512-yOY8IC2go9lfbVDMiy2ATun4EB2AFwocPaQADwMN/RHRUAZSM4rlAV7PGbWPSG/YhkJ2A9xQAiAENgSua9G5Fg==",
+ "requires": {
+ "@firebase/app-types": "0.9.3",
+ "@firebase/util": "1.15.0"
+ }
+ },
+ "@firebase/logger": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz",
+ "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "@firebase/util": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.0.tgz",
+ "integrity": "sha512-AmWf3cHAOMbrCPG4xdPKQaj5iHnyYfyLKZxwz+Xf55bqKbpAmcYifB4jQinT2W9XhDRHISOoPyBOariJpCG6FA==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "@google-cloud/firestore": {
+ "version": "7.11.6",
+ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz",
+ "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==",
+ "optional": true,
+ "requires": {
+ "@opentelemetry/api": "^1.3.0",
+ "fast-deep-equal": "^3.1.1",
+ "functional-red-black-tree": "^1.0.1",
+ "google-gax": "^4.3.3",
+ "protobufjs": "^7.2.6"
+ }
+ },
+ "@google-cloud/paginator": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
+ "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
+ "optional": true,
+ "requires": {
+ "arrify": "^2.0.0",
+ "extend": "^3.0.2"
+ }
+ },
+ "@google-cloud/projectify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
+ "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
+ "optional": true
+ },
+ "@google-cloud/promisify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
+ "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
+ "optional": true
+ },
+ "@google-cloud/storage": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz",
+ "integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==",
+ "optional": true,
+ "requires": {
+ "@google-cloud/paginator": "^5.0.0",
+ "@google-cloud/projectify": "^4.0.0",
+ "@google-cloud/promisify": "<4.1.0",
+ "abort-controller": "^3.0.0",
+ "async-retry": "^1.3.3",
+ "duplexify": "^4.1.3",
+ "fast-xml-parser": "^5.3.4",
+ "gaxios": "^6.0.2",
+ "google-auth-library": "^9.6.3",
+ "html-entities": "^2.5.2",
+ "mime": "^3.0.0",
+ "p-limit": "^3.0.1",
+ "retry-request": "^7.0.0",
+ "teeny-request": "^9.0.0",
+ "uuid": "^8.0.0"
+ },
+ "dependencies": {
+ "gcp-metadata": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+ "optional": true,
+ "requires": {
+ "gaxios": "^6.1.1",
+ "google-logging-utils": "^0.0.2",
+ "json-bigint": "^1.0.0"
+ }
+ },
+ "google-auth-library": {
+ "version": "9.15.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "optional": true,
+ "requires": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^6.1.1",
+ "gcp-metadata": "^6.1.0",
+ "gtoken": "^7.0.0",
+ "jws": "^4.0.0"
+ }
+ },
+ "google-logging-utils": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+ "optional": true
+ },
+ "mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "optional": true
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "optional": true
+ }
+ }
+ },
+ "@graphql-tools/merge": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz",
+ "integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==",
+ "requires": {
+ "@graphql-tools/utils": "^11.0.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "@graphql-tools/schema": {
+ "version": "10.0.31",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz",
+ "integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==",
+ "requires": {
+ "@graphql-tools/merge": "^9.1.7",
+ "@graphql-tools/utils": "^11.0.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "@graphql-tools/utils": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz",
+ "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==",
+ "requires": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@whatwg-node/promise-helpers": "^1.0.0",
+ "cross-inspect": "1.0.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "@graphql-typed-document-node/core": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz",
+ "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==",
+ "requires": {}
+ },
+ "@grpc/grpc-js": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
+ "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
+ "optional": true,
+ "requires": {
+ "@grpc/proto-loader": "^0.8.0",
+ "@js-sdsl/ordered-map": "^4.4.2"
+ },
+ "dependencies": {
+ "@grpc/proto-loader": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
+ "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
+ "optional": true,
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.5.3",
+ "yargs": "^17.7.2"
+ }
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "optional": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "optional": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "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==",
+ "optional": 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==",
+ "optional": true
+ },
+ "long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "optional": true
+ },
+ "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==",
+ "optional": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "optional": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "optional": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ }
+ }
+ },
+ "@grpc/proto-loader": {
+ "version": "0.7.15",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+ "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+ "optional": true,
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "optional": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "optional": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "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==",
+ "optional": 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==",
+ "optional": true
+ },
+ "long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "optional": true
+ },
+ "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==",
+ "optional": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "optional": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "optional": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ }
+ }
+ },
+ "@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true
+ },
+ "@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "requires": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "dependencies": {
+ "@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true
+ }
+ }
+ },
+ "@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/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "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==",
+ "devOptional": 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.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "devOptional": 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==",
+ "devOptional": 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==",
+ "devOptional": 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==",
+ "devOptional": true,
+ "requires": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "devOptional": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ },
+ "wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "devOptional": true,
+ "requires": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.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.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "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
+ }
+ }
+ },
+ "@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
+ },
+ "@jasminejs/reporters": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@jasminejs/reporters/-/reporters-1.0.0.tgz",
+ "integrity": "sha512-rM3GG4vx2H1Gp5kYCTr9aKlOEJFd43pzpiMAiy5b1+FUc2ub4e6bS6yCi/WQNDzAa5MVp9++dwcoEtcIfoEnhA==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "@js-sdsl/ordered-map": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
+ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
+ "optional": true
+ },
+ "@jsdoc/salty": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
+ "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.21"
+ }
+ },
+ "@ldapjs/asn1": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz",
+ "integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA=="
+ },
+ "@ldapjs/attribute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz",
+ "integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==",
+ "requires": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "@ldapjs/change": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz",
+ "integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==",
+ "requires": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/attribute": "1.0.0"
+ }
+ },
+ "@ldapjs/controls": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz",
+ "integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==",
+ "requires": {
+ "@ldapjs/asn1": "^1.2.0",
+ "@ldapjs/protocol": "^1.2.1"
+ },
+ "dependencies": {
+ "@ldapjs/asn1": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz",
+ "integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw=="
+ }
+ }
+ },
+ "@ldapjs/dn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz",
+ "integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==",
+ "requires": {
+ "@ldapjs/asn1": "2.0.0",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "@ldapjs/filter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz",
+ "integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==",
+ "requires": {
+ "@ldapjs/asn1": "2.0.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.1.0"
+ }
+ },
+ "@ldapjs/messages": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz",
+ "integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==",
+ "requires": {
+ "@ldapjs/asn1": "^2.0.0",
+ "@ldapjs/attribute": "^1.0.0",
+ "@ldapjs/change": "^1.0.0",
+ "@ldapjs/controls": "^2.1.0",
+ "@ldapjs/dn": "^1.1.0",
+ "@ldapjs/filter": "^2.1.1",
+ "@ldapjs/protocol": "^1.2.1",
+ "process-warning": "^2.2.0"
+ }
+ },
+ "@ldapjs/protocol": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz",
+ "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ=="
+ },
+ "@mongodb-js/mongodb-downloader": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.4.2.tgz",
+ "integrity": "sha512-uCd6nDtKuM2J12jgqPkApEvGQWfgZOq6yUitagvXYIqg6ofdqAnmMJO3e3wIph+Vi++dnLoMv0ME9geBzHYwDA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.4.0",
+ "decompress": "^4.2.1",
+ "mongodb-download-url": "^1.6.2",
+ "node-fetch": "^2.7.0",
+ "tar": "^6.1.15"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true,
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+ },
+ "@mongodb-js/saslprep": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
+ "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
+ "requires": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
+ "@napi-rs/wasm-runtime": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.5.tgz",
+ "integrity": "sha512-kwUxR7J9WLutBbulqg1dfOrMTwhMdXLdcGUhcbCcGwnPLt3gz19uHVdwH1syKVDbE022ZS2vZxOWflFLS0YTjw==",
+ "optional": true,
+ "requires": {
+ "@emnapi/core": "^1.1.0",
+ "@emnapi/runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.9.0"
+ }
+ },
+ "@nicolo-ribaudo/chokidar-2": {
+ "version": "2.1.8-no-fsevents.3",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
+ "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@nicolo-ribaudo/eslint-scope-5-internals": {
+ "version": "5.1.1-v1",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
+ "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
+ "dev": true,
+ "requires": {
+ "eslint-scope": "5.1.1"
+ }
+ },
+ "@noble/hashes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz",
+ "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="
+ },
+ "@node-rs/bcrypt": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.10.7.tgz",
+ "integrity": "sha512-1wk0gHsUQC/ap0j6SJa2K34qNhomxXRcEe3T8cI5s+g6fgHBgLTN7U9LzWTG/HE6G4+2tWWLeCabk1wiYGEQSA==",
+ "optional": true,
+ "requires": {
+ "@node-rs/bcrypt-android-arm-eabi": "1.10.7",
+ "@node-rs/bcrypt-android-arm64": "1.10.7",
+ "@node-rs/bcrypt-darwin-arm64": "1.10.7",
+ "@node-rs/bcrypt-darwin-x64": "1.10.7",
+ "@node-rs/bcrypt-freebsd-x64": "1.10.7",
+ "@node-rs/bcrypt-linux-arm-gnueabihf": "1.10.7",
+ "@node-rs/bcrypt-linux-arm64-gnu": "1.10.7",
+ "@node-rs/bcrypt-linux-arm64-musl": "1.10.7",
+ "@node-rs/bcrypt-linux-x64-gnu": "1.10.7",
+ "@node-rs/bcrypt-linux-x64-musl": "1.10.7",
+ "@node-rs/bcrypt-wasm32-wasi": "1.10.7",
+ "@node-rs/bcrypt-win32-arm64-msvc": "1.10.7",
+ "@node-rs/bcrypt-win32-ia32-msvc": "1.10.7",
+ "@node-rs/bcrypt-win32-x64-msvc": "1.10.7"
+ }
+ },
+ "@node-rs/bcrypt-android-arm-eabi": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.10.7.tgz",
+ "integrity": "sha512-8dO6/PcbeMZXS3VXGEtct9pDYdShp2WBOWlDvSbcRwVqyB580aCBh0BEFmKYtXLzLvUK8Wf+CG3U6sCdILW1lA==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-android-arm64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.10.7.tgz",
+ "integrity": "sha512-UASFBS/CucEMHiCtL/2YYsAY01ZqVR1N7vSb94EOvG5iwW7BQO06kXXCTgj+Xbek9azxixrCUmo3WJnkJZ0hTQ==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-darwin-arm64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.10.7.tgz",
+ "integrity": "sha512-DgzFdAt455KTuiJ/zYIyJcKFobjNDR/hnf9OS7pK5NRS13Nq4gLcSIIyzsgHwZHxsJWbLpHmFc1H23Y7IQoQBw==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-darwin-x64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.10.7.tgz",
+ "integrity": "sha512-SPWVfQ6sxSokoUWAKWD0EJauvPHqOGQTd7CxmYatcsUgJ/bruvEHxZ4bIwX1iDceC3FkOtmeHO0cPwR480n/xA==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-freebsd-x64": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.10.7.tgz",
+ "integrity": "sha512-gpa+Ixs6GwEx6U6ehBpsQetzUpuAGuAFbOiuLB2oo4N58yU4AZz1VIcWyWAHrSWRs92O0SHtmo2YPrMrwfBbSw==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-linux-arm-gnueabihf": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.10.7.tgz",
+ "integrity": "sha512-kYgJnTnpxrzl9sxYqzflobvMp90qoAlaX1oDL7nhNTj8OYJVDIk0jQgblj0bIkjmoPbBed53OJY/iu4uTS+wig==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-linux-arm64-gnu": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.10.7.tgz",
+ "integrity": "sha512-7cEkK2RA+gBCj2tCVEI1rDSJV40oLbSq7bQ+PNMHNI6jCoXGmj9Uzo7mg7ZRbNZ7piIyNH5zlJqutjo8hh/tmA==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-linux-arm64-musl": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.10.7.tgz",
+ "integrity": "sha512-X7DRVjshhwxUqzdUKDlF55cwzh+wqWJ2E/tILvZPboO3xaNO07Um568Vf+8cmKcz+tiZCGP7CBmKbBqjvKN/Pw==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-linux-x64-gnu": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.10.7.tgz",
+ "integrity": "sha512-LXRZsvG65NggPD12hn6YxVgH0W3VR5fsE/o1/o2D5X0nxKcNQGeLWnRzs5cP8KpoFOuk1ilctXQJn8/wq+Gn/Q==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-linux-x64-musl": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.10.7.tgz",
+ "integrity": "sha512-tCjHmct79OfcO3g5q21ME7CNzLzpw1MAsUXCLHLGWH+V6pp/xTvMbIcLwzkDj6TI3mxK6kehTn40SEjBkZ3Rog==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-wasm32-wasi": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.10.7.tgz",
+ "integrity": "sha512-4qXSihIKeVXYglfXZEq/QPtYtBUvR8d3S85k15Lilv3z5B6NSGQ9mYiNleZ7QHVLN2gEc5gmi7jM353DMH9GkA==",
+ "optional": true,
+ "requires": {
+ "@napi-rs/wasm-runtime": "^0.2.5"
+ }
+ },
+ "@node-rs/bcrypt-win32-arm64-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.10.7.tgz",
+ "integrity": "sha512-FdfUQrqmDfvC5jFhntMBkk8EI+fCJTx/I1v7Rj+Ezlr9rez1j1FmuUnywbBj2Cg15/0BDhwYdbyZ5GCMFli2aQ==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-win32-ia32-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.10.7.tgz",
+ "integrity": "sha512-lZLf4Cx+bShIhU071p5BZft4OvP4PGhyp542EEsb3zk34U5GLsGIyCjOafcF/2DGewZL6u8/aqoxbSuROkgFXg==",
+ "optional": true
+ },
+ "@node-rs/bcrypt-win32-x64-msvc": {
+ "version": "1.10.7",
+ "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.10.7.tgz",
+ "integrity": "sha512-hdw7tGmN1DxVAMTzICLdaHpXjy+4rxaxnBMgI8seG1JL5e3VcRGsd1/1vVDogVp2cbsmgq+6d6yAY+D9lW/DCg==",
+ "optional": true
+ },
+ "@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"
+ }
+ },
+ "@octokit/auth-token": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
+ "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
+ "dev": true
+ },
+ "@octokit/core": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz",
+ "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==",
+ "dev": true,
+ "requires": {
+ "@octokit/auth-token": "^6.0.0",
+ "@octokit/graphql": "^9.0.1",
+ "@octokit/request": "^10.0.2",
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "before-after-hook": "^4.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/endpoint": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz",
+ "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^14.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/graphql": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz",
+ "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==",
+ "dev": true,
+ "requires": {
+ "@octokit/request": "^10.0.2",
+ "@octokit/types": "^14.0.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
+ "dev": true
+ },
+ "@octokit/plugin-paginate-rest": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
+ "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^16.0.0"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "27.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
+ "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
+ "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^27.0.0"
+ }
+ }
+ }
+ },
+ "@octokit/plugin-retry": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz",
+ "integrity": "sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw==",
+ "dev": true,
+ "requires": {
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/plugin-throttling": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz",
+ "integrity": "sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^14.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/request": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz",
+ "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==",
+ "dev": true,
+ "requires": {
+ "@octokit/endpoint": "^11.0.0",
+ "@octokit/request-error": "^7.0.0",
+ "@octokit/types": "^14.0.0",
+ "fast-content-type-parse": "^3.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/request-error": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz",
+ "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^14.0.0"
+ },
+ "dependencies": {
+ "@octokit/openapi-types": {
+ "version": "25.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz",
+ "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==",
+ "dev": true
+ },
+ "@octokit/types": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz",
+ "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^25.1.0"
+ }
+ }
+ }
+ },
+ "@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^22.2.0"
+ }
+ },
+ "@opentelemetry/api": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+ "optional": true
+ },
+ "@parse/fs-files-adapter": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
+ "integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
+ },
+ "@parse/node-apn": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-8.0.0.tgz",
+ "integrity": "sha512-blvU/V0FL3j7u2lstso1aInMw7yYrKg/6Ctr3Kc/7kleFatAfZswhzHk9d5lI4DUQBsUBun8nidgZHCY6sft+Q==",
+ "requires": {
+ "debug": "4.4.3",
+ "jsonwebtoken": "9.0.3",
+ "node-forge": "1.4.0",
+ "verror": "1.10.1"
+ }
+ },
+ "@parse/push-adapter": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.4.0.tgz",
+ "integrity": "sha512-sWinUJZvbWIH6cJfIRuwUCcsjvi6IkoJ3zp2JoCP/mLzItt6NPNk+j73RE9UJzIKlwt3NciWXeSHoxprPnNH/A==",
+ "requires": {
+ "@parse/node-apn": "8.0.0",
+ "expo-server-sdk": "6.1.0",
+ "firebase-admin": "13.7.0",
+ "npmlog": "7.0.1",
+ "parse": "8.5.0",
+ "web-push": "3.6.7"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="
+ },
+ "@babel/runtime-corejs3": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz",
+ "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==",
+ "requires": {
+ "core-js-pure": "^3.48.0"
+ }
+ },
+ "parse": {
+ "version": "8.5.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.5.0.tgz",
+ "integrity": "sha512-X9gI4Yjbi9LPMPnCtKL4h0Nxe1aSCFMPWcB1zbu11qU/Be3eVSB5I5IMBunTuWlVz6Wchu3dtM5jl/1aBZ9wiQ==",
+ "requires": {
+ "@babel/runtime": "7.28.6",
+ "@babel/runtime-corejs3": "7.29.0",
+ "crypto-js": "4.2.0",
+ "idb-keyval": "6.2.2",
+ "react-native-crypto-js": "1.0.0",
+ "ws": "8.19.0"
+ }
+ },
+ "ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "requires": {}
+ }
+ }
+ },
+ "@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
+ },
+ "@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+ "dev": true
+ },
+ "@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.2.10"
+ }
+ },
+ "@pnpm/npm-conf": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
+ "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
+ "dev": true,
+ "requires": {
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ }
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "@redis/bloom": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.11.0.tgz",
+ "integrity": "sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==",
+ "requires": {}
+ },
+ "@redis/client": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.11.0.tgz",
+ "integrity": "sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==",
+ "requires": {
+ "cluster-key-slot": "1.1.2"
+ }
+ },
+ "@redis/json": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.11.0.tgz",
+ "integrity": "sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==",
+ "requires": {}
+ },
+ "@redis/search": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.11.0.tgz",
+ "integrity": "sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==",
+ "requires": {}
+ },
+ "@redis/time-series": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.11.0.tgz",
+ "integrity": "sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==",
+ "requires": {}
+ },
+ "@saithodev/semantic-release-backmerge": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@saithodev/semantic-release-backmerge/-/semantic-release-backmerge-4.0.1.tgz",
+ "integrity": "sha512-WDsU28YrXSLx0xny7FgFlEk8DCKGcj6OOhA+4Q9k3te1jJD1GZuqY8sbIkVQaw9cqJ7CT+fCZUN6QDad8JW4Dg==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.1.0",
+ "debug": "^4.3.4",
+ "execa": "^5.1.1",
+ "lodash": "^4.17.21",
+ "semantic-release": "^22.0.7"
+ },
+ "dependencies": {
+ "@octokit/auth-token": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
+ "dev": true
+ },
+ "@octokit/core": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
+ "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
+ "dev": true,
+ "requires": {
+ "@octokit/auth-token": "^4.0.0",
+ "@octokit/graphql": "^7.1.0",
+ "@octokit/request": "^8.3.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/endpoint": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
+ "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/graphql": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
+ "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
+ "dev": true,
+ "requires": {
+ "@octokit/request": "^8.3.0",
+ "@octokit/types": "^13.0.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/openapi-types": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
+ "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
+ "dev": true
+ },
+ "@octokit/plugin-paginate-rest": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
+ "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^12.6.0"
+ },
+ "dependencies": {
+ "@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ }
+ }
+ },
+ "@octokit/plugin-retry": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
+ "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==",
+ "dev": true,
+ "requires": {
+ "@octokit/request-error": "^5.0.0",
+ "@octokit/types": "^12.0.0",
+ "bottleneck": "^2.15.3"
+ },
+ "dependencies": {
+ "@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ }
+ }
+ },
+ "@octokit/plugin-throttling": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz",
+ "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^12.2.0",
+ "bottleneck": "^2.15.3"
+ },
+ "dependencies": {
+ "@octokit/types": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
+ "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
+ "dev": true,
+ "requires": {
+ "@octokit/openapi-types": "^20.0.0"
+ }
+ }
+ }
+ },
+ "@octokit/request": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
+ "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
+ "dev": true,
+ "requires": {
+ "@octokit/endpoint": "^9.0.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/request-error": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
+ "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
+ "dev": true,
+ "requires": {
+ "@octokit/types": "^13.1.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
+ }
+ },
+ "@semantic-release/commit-analyzer": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-11.1.0.tgz",
+ "integrity": "sha512-cXNTbv3nXR2hlzHjAMgbuiQVtvWHTlwwISt60B+4NZv01y/QRY7p2HcJm8Eh2StzcTJoNnflvKjHH/cjFS7d5g==",
+ "dev": true,
+ "requires": {
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-commits-filter": "^4.0.0",
+ "conventional-commits-parser": "^5.0.0",
+ "debug": "^4.0.0",
+ "import-from-esm": "^1.0.3",
+ "lodash-es": "^4.17.21",
+ "micromatch": "^4.0.2"
+ }
+ },
+ "@semantic-release/github": {
+ "version": "9.2.6",
+ "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.6.tgz",
+ "integrity": "sha512-shi+Lrf6exeNZF+sBhK+P011LSbhmIAoUEgEY6SsxF8irJ+J2stwI5jkyDQ+4gzYyDImzV6LCKdYB9FXnQRWKA==",
+ "dev": true,
+ "requires": {
+ "@octokit/core": "^5.0.0",
+ "@octokit/plugin-paginate-rest": "^9.0.0",
+ "@octokit/plugin-retry": "^6.0.0",
+ "@octokit/plugin-throttling": "^8.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "debug": "^4.3.4",
+ "dir-glob": "^3.0.1",
+ "globby": "^14.0.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "issue-parser": "^6.0.0",
+ "lodash-es": "^4.17.21",
+ "mime": "^4.0.0",
+ "p-filter": "^4.0.0",
+ "url-join": "^5.0.0"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ }
+ }
+ },
+ "@semantic-release/npm": {
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-11.0.3.tgz",
+ "integrity": "sha512-KUsozQGhRBAnoVg4UMZj9ep436VEGwT536/jwSqB7vcEfA6oncCUU7UIYTRdLx7GvTtqn0kBjnkfLVkcnBa2YQ==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "execa": "^8.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^8.0.0",
+ "npm": "^10.5.0",
+ "rc": "^1.2.8",
+ "read-pkg": "^9.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ },
+ "execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true
+ }
+ }
+ },
+ "@semantic-release/release-notes-generator": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-12.1.0.tgz",
+ "integrity": "sha512-g6M9AjUKAZUZnxaJZnouNBeDNTCUrJ5Ltj+VJ60gJeDaRRahcHsry9HW8yKrnKkKNkx5lbWiEP1FPMqVNQz8Kg==",
+ "dev": true,
+ "requires": {
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-changelog-writer": "^7.0.0",
+ "conventional-commits-filter": "^4.0.0",
+ "conventional-commits-parser": "^5.0.0",
+ "debug": "^4.0.0",
+ "get-stream": "^7.0.0",
+ "import-from-esm": "^1.0.3",
+ "into-stream": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "read-pkg-up": "^11.0.0"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz",
+ "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-escapes": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
+ "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "before-after-hook": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true
+ },
+ "clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "5.0.0"
+ }
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true
+ },
+ "conventional-changelog-angular": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+ "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+ "dev": true,
+ "requires": {
+ "compare-func": "^2.0.0"
+ }
+ },
+ "conventional-changelog-writer": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.1.tgz",
+ "integrity": "sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==",
+ "dev": true,
+ "requires": {
+ "conventional-commits-filter": "^4.0.0",
+ "handlebars": "^4.7.7",
+ "json-stringify-safe": "^5.0.1",
+ "meow": "^12.0.1",
+ "semver": "^7.5.2",
+ "split2": "^4.0.0"
+ }
+ },
+ "conventional-commits-filter": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz",
+ "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==",
+ "dev": true
+ },
+ "conventional-commits-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+ "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+ "dev": true,
+ "requires": {
+ "is-text-path": "^2.0.0",
+ "JSONStream": "^1.3.5",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ }
+ },
+ "cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "requires": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ }
+ },
+ "env-ci": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-10.0.0.tgz",
+ "integrity": "sha512-U4xcd/utDYFgMh0yWj07R1H6L5fwhVbmxBCpnL0DbVSDZVnsC82HONw0wxtxNkIAcua3KtbomQvIk5xFZGAQJw==",
+ "dev": true,
+ "requires": {
+ "execa": "^8.0.0",
+ "java-properties": "^1.0.2"
+ },
+ "dependencies": {
+ "execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true
+ }
+ }
+ },
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true
+ },
+ "find-versions": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz",
+ "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==",
+ "dev": true,
+ "requires": {
+ "semver-regex": "^4.0.5"
+ }
+ },
+ "globby": {
+ "version": "14.0.2",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz",
+ "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==",
+ "dev": true,
+ "requires": {
+ "@sindresorhus/merge-streams": "^2.1.0",
+ "fast-glob": "^3.3.2",
+ "ignore": "^5.2.4",
+ "path-type": "^5.0.0",
+ "slash": "^5.1.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "dependencies": {
+ "path-type": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
+ "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
+ "dev": true
+ }
+ }
+ },
+ "human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true
+ },
+ "issue-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz",
+ "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==",
+ "dev": true,
+ "requires": {
+ "lodash.capitalize": "^4.2.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.uniqby": "^4.7.0"
+ }
+ },
+ "marked": {
+ "version": "9.1.6",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
+ "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==",
+ "dev": true
+ },
+ "marked-terminal": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
+ "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^6.2.0",
+ "cardinal": "^2.1.1",
+ "chalk": "^5.3.0",
+ "cli-table3": "^0.6.3",
+ "node-emoji": "^2.1.3",
+ "supports-hyperlinks": "^3.0.0"
+ }
+ },
+ "meow": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+ "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ }
+ },
+ "onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^4.0.0"
+ }
+ },
+ "p-reduce": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
+ "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true
+ },
+ "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
+ },
+ "semantic-release": {
+ "version": "22.0.12",
+ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.12.tgz",
+ "integrity": "sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/commit-analyzer": "^11.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "@semantic-release/github": "^9.0.0",
+ "@semantic-release/npm": "^11.0.0",
+ "@semantic-release/release-notes-generator": "^12.0.0",
+ "aggregate-error": "^5.0.0",
+ "cosmiconfig": "^8.0.0",
+ "debug": "^4.0.0",
+ "env-ci": "^10.0.0",
+ "execa": "^8.0.0",
+ "figures": "^6.0.0",
+ "find-versions": "^5.1.0",
+ "get-stream": "^6.0.0",
+ "git-log-parser": "^1.2.0",
+ "hook-std": "^3.0.0",
+ "hosted-git-info": "^7.0.0",
+ "import-from-esm": "^1.3.1",
+ "lodash-es": "^4.17.21",
+ "marked": "^9.0.0",
+ "marked-terminal": "^6.0.0",
+ "micromatch": "^4.0.2",
+ "p-each-series": "^3.0.0",
+ "p-reduce": "^3.0.0",
+ "read-pkg-up": "^11.0.0",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.3.2",
+ "semver-diff": "^4.0.0",
+ "signale": "^1.2.1",
+ "yargs": "^17.5.1"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ },
+ "execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "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
+ },
+ "slash": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
+ "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+ "dev": true
+ },
+ "strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true
+ },
+ "universal-user-agent": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ }
+ }
+ },
+ "@sec-ant/readable-stream": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
+ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
+ "dev": true
+ },
+ "@semantic-release/changelog": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz",
+ "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "@semantic-release/commit-analyzer": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz",
+ "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==",
+ "dev": true,
+ "requires": {
+ "conventional-changelog-angular": "^8.0.0",
+ "conventional-changelog-writer": "^8.0.0",
+ "conventional-commits-filter": "^5.0.0",
+ "conventional-commits-parser": "^6.0.0",
+ "debug": "^4.0.0",
+ "import-from-esm": "^2.0.0",
+ "lodash-es": "^4.17.21",
+ "micromatch": "^4.0.2"
+ },
+ "dependencies": {
+ "import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ }
+ }
+ }
+ },
+ "@semantic-release/error": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz",
+ "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==",
+ "dev": true
+ },
+ "@semantic-release/git": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz",
+ "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/error": "^3.0.0",
+ "aggregate-error": "^3.0.0",
+ "debug": "^4.0.0",
+ "dir-glob": "^3.0.0",
+ "execa": "^5.0.0",
+ "lodash": "^4.17.4",
+ "micromatch": "^4.0.0",
+ "p-reduce": "^2.0.0"
+ }
+ },
+ "@semantic-release/github": {
+ "version": "12.0.6",
+ "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-12.0.6.tgz",
+ "integrity": "sha512-aYYFkwHW3c6YtHwQF0t0+lAjlU+87NFOZuH2CvWFD0Ylivc7MwhZMiHOJ0FMpIgPpCVib/VUAcOwvrW0KnxQtA==",
+ "dev": true,
+ "requires": {
+ "@octokit/core": "^7.0.0",
+ "@octokit/plugin-paginate-rest": "^14.0.0",
+ "@octokit/plugin-retry": "^8.0.0",
+ "@octokit/plugin-throttling": "^11.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "debug": "^4.3.4",
+ "dir-glob": "^3.0.1",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.0",
+ "issue-parser": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "mime": "^4.0.0",
+ "p-filter": "^4.0.0",
+ "tinyglobby": "^0.2.14",
+ "undici": "^7.0.0",
+ "url-join": "^5.0.0"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ },
+ "clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "5.0.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true
+ },
+ "undici": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz",
+ "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==",
+ "dev": true
+ }
+ }
+ },
+ "@semantic-release/npm": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-13.0.0.tgz",
+ "integrity": "sha512-7RIx9nUdUekYbIZ0dG7k7G/iSvUCZb03LmmBPFqAQEhPVC+BnHfhFxj5ewSNP6zMUsYaEQSckcOhKD8AuS/EzQ==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "execa": "^9.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^8.0.0",
+ "npm": "^11.6.2",
+ "rc": "^1.2.8",
+ "read-pkg": "^9.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ },
+ "clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "5.0.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true
+ },
+ "execa": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
+ "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
+ "dev": true,
+ "requires": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "cross-spawn": "^7.0.3",
+ "figures": "^6.1.0",
+ "get-stream": "^9.0.0",
+ "human-signals": "^7.0.0",
+ "is-plain-obj": "^4.1.0",
+ "is-stream": "^4.0.1",
+ "npm-run-path": "^5.2.0",
+ "pretty-ms": "^9.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^4.0.0",
+ "yoctocolors": "^2.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "dev": true,
+ "requires": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ }
+ },
+ "human-signals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
+ "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "dev": true
+ },
+ "npm": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-11.12.0.tgz",
+ "integrity": "sha512-xPhOap4ZbJWyd7DAOukP564WFwNSGu/2FeTRFHhiiKthcauxhH/NpkJAQm24xD+cAn8av5tQ00phi98DqtfLsg==",
+ "dev": true,
+ "requires": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/config": "^10.8.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/map-workspaces": "^5.0.3",
+ "@npmcli/metavuln-calculator": "^9.0.3",
+ "@npmcli/package-json": "^7.0.5",
+ "@npmcli/promise-spawn": "^9.0.1",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.4",
+ "@sigstore/tuf": "^4.0.2",
+ "abbrev": "^4.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^20.0.4",
+ "chalk": "^5.6.2",
+ "ci-info": "^4.4.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^13.0.6",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^9.0.2",
+ "ini": "^6.0.0",
+ "init-package-json": "^8.2.5",
+ "is-cidr": "^6.0.3",
+ "json-parse-even-better-errors": "^5.0.0",
+ "libnpmaccess": "^10.0.3",
+ "libnpmdiff": "^8.1.5",
+ "libnpmexec": "^10.2.5",
+ "libnpmfund": "^7.0.19",
+ "libnpmorg": "^8.0.1",
+ "libnpmpack": "^9.1.5",
+ "libnpmpublish": "^11.1.3",
+ "libnpmsearch": "^9.0.1",
+ "libnpmteam": "^8.0.2",
+ "libnpmversion": "^8.0.3",
+ "make-fetch-happen": "^15.0.5",
+ "minimatch": "^10.2.4",
+ "minipass": "^7.1.3",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^12.2.0",
+ "nopt": "^9.0.0",
+ "npm-audit-report": "^7.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.2",
+ "npm-pick-manifest": "^11.0.3",
+ "npm-profile": "^12.0.1",
+ "npm-registry-fetch": "^19.1.1",
+ "npm-user-validate": "^4.0.0",
+ "p-map": "^7.0.4",
+ "pacote": "^21.5.0",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.1.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^5.0.1",
+ "semver": "^7.7.4",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^13.0.1",
+ "supports-color": "^10.2.2",
+ "tar": "^7.5.11",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^2.0.2",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^7.0.2",
+ "which": "^6.0.1"
+ },
+ "dependencies": {
+ "@gar/promise-retry": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.4"
+ }
+ },
+ "@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/agent": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^11.2.1",
+ "socks-proxy-agent": "^8.0.3"
+ }
+ },
+ "@npmcli/arborist": {
+ "version": "9.4.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/metavuln-calculator": "^9.0.2",
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/query": "^5.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "bin-links": "^6.0.0",
+ "cacache": "^20.0.1",
+ "common-ancestor-path": "^2.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^11.2.1",
+ "minimatch": "^10.0.3",
+ "nopt": "^9.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "pacote": "^21.0.2",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.0.0",
+ "proggy": "^4.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "semver": "^7.3.7",
+ "ssri": "^13.0.0",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "@npmcli/config": {
+ "version": "10.8.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "ini": "^6.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "@npmcli/fs": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/git": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "ini": "^6.0.0",
+ "lru-cache": "^11.2.1",
+ "npm-pick-manifest": "^11.0.1",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "which": "^6.0.0"
+ }
+ },
+ "@npmcli/installed-package-contents": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-bundled": "^5.0.0",
+ "npm-normalize-package-bin": "^5.0.0"
+ }
+ },
+ "@npmcli/map-workspaces": {
+ "version": "5.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "glob": "^13.0.0",
+ "minimatch": "^10.0.3"
+ }
+ },
+ "@npmcli/metavuln-calculator": {
+ "version": "9.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cacache": "^20.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "pacote": "^21.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/name-from-folder": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/node-gyp": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/package-json": {
+ "version": "7.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^7.0.0",
+ "glob": "^13.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.5.3",
+ "spdx-expression-parse": "^4.0.0"
+ }
+ },
+ "@npmcli/promise-spawn": {
+ "version": "9.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "which": "^6.0.0"
+ }
+ },
+ "@npmcli/query": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
+ "@npmcli/redact": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/run-script": {
+ "version": "10.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "node-gyp": "^12.1.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "@sigstore/bundle": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.5.0"
+ }
+ },
+ "@sigstore/core": {
+ "version": "3.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/protobuf-specs": {
+ "version": "0.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/sign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.2",
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.2.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "make-fetch-happen": "^15.0.4",
+ "proc-log": "^6.1.0"
+ }
+ },
+ "@sigstore/tuf": {
+ "version": "4.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "tuf-js": "^4.1.0"
+ }
+ },
+ "@sigstore/verify": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0"
+ }
+ },
+ "@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@tufjs/models": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^10.1.1"
+ }
+ },
+ "abbrev": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "agent-base": {
+ "version": "7.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "archy": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "bundled": true,
+ "dev": true
+ },
+ "bin-links": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cmd-shim": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "read-cmd-shim": "^6.0.0",
+ "write-file-atomic": "^7.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "cacache": {
+ "version": "20.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^5.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^13.0.0",
+ "lru-cache": "^11.1.0",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^7.0.2",
+ "ssri": "^13.0.0"
+ }
+ },
+ "chalk": {
+ "version": "5.6.2",
+ "bundled": true,
+ "dev": true
+ },
+ "chownr": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ci-info": {
+ "version": "4.4.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cidr-regex": {
+ "version": "5.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "cmd-shim": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "common-ancestor-path": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "debug": {
+ "version": "4.4.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.3"
+ }
+ },
+ "diff": {
+ "version": "8.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "bundled": true,
+ "dev": true
+ },
+ "exponential-backoff": {
+ "version": "3.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "fastest-levenshtein": {
+ "version": "1.0.16",
+ "bundled": true,
+ "dev": true
+ },
+ "fs-minipass": {
+ "version": "3.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "glob": {
+ "version": "13.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "bundled": true,
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "9.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^11.1.0"
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "7.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.7.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "ignore-walk": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimatch": "^10.0.3"
+ }
+ },
+ "ini": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "init-package-json": {
+ "version": "8.2.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/package-json": "^7.0.0",
+ "npm-package-arg": "^13.0.0",
+ "promzard": "^3.0.1",
+ "read": "^5.0.1",
+ "semver": "^7.7.2",
+ "validate-npm-package-name": "^7.0.0"
+ }
+ },
+ "ip-address": {
+ "version": "10.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "is-cidr": {
+ "version": "6.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cidr-regex": "^5.0.1"
+ }
+ },
+ "isexe": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "json-stringify-nice": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff": {
+ "version": "6.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff-apply": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "libnpmaccess": {
+ "version": "10.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmdiff": {
+ "version": "8.1.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "binary-extensions": "^3.0.0",
+ "diff": "^8.0.2",
+ "minimatch": "^10.0.3",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "tar": "^7.5.1"
+ }
+ },
+ "libnpmexec": {
+ "version": "10.2.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "proc-log": "^6.0.0",
+ "read": "^5.0.1",
+ "semver": "^7.3.7",
+ "signal-exit": "^4.1.0",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "libnpmfund": {
+ "version": "7.0.19",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2"
+ }
+ },
+ "libnpmorg": {
+ "version": "8.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmpack": {
+ "version": "9.1.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/run-script": "^10.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2"
+ }
+ },
+ "libnpmpublish": {
+ "version": "11.1.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0"
+ }
+ },
+ "libnpmsearch": {
+ "version": "9.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmteam": {
+ "version": "8.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmversion": {
+ "version": "8.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7"
+ }
+ },
+ "lru-cache": {
+ "version": "11.2.7",
+ "bundled": true,
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "15.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/agent": "^4.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "cacache": "^20.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^1.0.0",
+ "proc-log": "^6.0.0",
+ "ssri": "^13.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "10.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.2"
+ }
+ },
+ "minipass": {
+ "version": "7.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass-collect": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "minipass-fetch": {
+ "version": "5.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "iconv-lite": "^0.7.2",
+ "minipass": "^7.0.3",
+ "minipass-sized": "^2.0.0",
+ "minizlib": "^3.0.1"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "minipass-sized": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.1.2"
+ }
+ },
+ "minizlib": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "negotiator": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "12.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^15.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.5.4",
+ "tinyglobby": "^0.2.12",
+ "which": "^6.0.0"
+ }
+ },
+ "nopt": {
+ "version": "9.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "abbrev": "^4.0.0"
+ }
+ },
+ "npm-audit-report": {
+ "version": "7.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^5.0.0"
+ }
+ },
+ "npm-install-checks": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "13.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^7.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "10.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^8.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "11.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "npm-package-arg": "^13.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "npm-profile": {
+ "version": "12.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "19.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/redact": "^4.0.0",
+ "jsonparse": "^1.3.1",
+ "make-fetch-happen": "^15.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minizlib": "^3.0.1",
+ "npm-package-arg": "^13.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-user-validate": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "p-map": {
+ "version": "7.0.4",
+ "bundled": true,
+ "dev": true
+ },
+ "pacote": {
+ "version": "21.5.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "cacache": "^20.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^13.0.0",
+ "npm-packlist": "^10.0.1",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0",
+ "tar": "^7.4.3"
+ }
+ },
+ "parse-conflict-json": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^5.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ }
+ },
+ "path-scurry": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "7.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "proc-log": {
+ "version": "6.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "proggy": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-all-reject-late": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-call-limit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "promzard": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "read": "^5.0.0"
+ }
+ },
+ "qrcode-terminal": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "read": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mute-stream": "^3.0.0"
+ }
+ },
+ "read-cmd-shim": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "7.7.4",
+ "bundled": true,
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "sigstore": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "@sigstore/sign": "^4.1.0",
+ "@sigstore/tuf": "^4.0.1",
+ "@sigstore/verify": "^3.1.0"
+ }
+ },
+ "smart-buffer": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "socks": {
+ "version": "2.8.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "8.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.23",
+ "bundled": true,
+ "dev": true
+ },
+ "ssri": {
+ "version": "13.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "supports-color": {
+ "version": "10.2.2",
+ "bundled": true,
+ "dev": true
+ },
+ "tar": {
+ "version": "7.5.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tiny-relative-date": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "tinyglobby": {
+ "version": "0.2.15",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "dependencies": {
+ "fdir": {
+ "version": "6.5.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {}
+ },
+ "picomatch": {
+ "version": "4.0.3",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "treeverse": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tuf-js": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/models": "4.1.0",
+ "debug": "^4.4.3",
+ "make-fetch-happen": "^15.0.1"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "validate-npm-package-name": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "walk-up-path": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "which": {
+ "version": "6.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "isexe": "^4.0.0"
+ }
+ },
+ "write-file-atomic": {
+ "version": "7.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "yallist": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ }
+ },
+ "parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true
+ },
+ "pretty-ms": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
+ "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
+ "dev": true,
+ "requires": {
+ "parse-ms": "^4.0.0"
+ }
+ },
+ "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
+ },
+ "strip-final-newline": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+ "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+ "dev": true
+ }
+ }
+ },
+ "@semantic-release/release-notes-generator": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.1.0.tgz",
+ "integrity": "sha512-CcyDRk7xq+ON/20YNR+1I/jP7BYKICr1uKd1HHpROSnnTdGqOTburi4jcRiTYz0cpfhxSloQO3cGhnoot7IEkA==",
+ "dev": true,
+ "requires": {
+ "conventional-changelog-angular": "^8.0.0",
+ "conventional-changelog-writer": "^8.0.0",
+ "conventional-commits-filter": "^5.0.0",
+ "conventional-commits-parser": "^6.0.0",
+ "debug": "^4.0.0",
+ "get-stream": "^7.0.0",
+ "import-from-esm": "^2.0.0",
+ "into-stream": "^7.0.0",
+ "lodash-es": "^4.17.21",
+ "read-package-up": "^11.0.0"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz",
+ "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==",
+ "dev": true
+ },
+ "import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.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==",
+ "dev": true
+ },
+ "@sindresorhus/merge-streams": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
+ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "dev": true
+ },
+ "@so-ric/colorspace": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
+ "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
+ "requires": {
+ "color": "^5.0.2",
+ "text-hex": "1.0.x"
+ }
+ },
+ "@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==",
+ "dev": true,
+ "requires": {
+ "defer-to-connect": "^2.0.1"
+ }
+ },
+ "@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "optional": true
+ },
+ "@ts-graphviz/adapter": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.5.tgz",
+ "integrity": "sha512-K/xd2SJskbSLcUz9uYW9IDy26I3Oyutj/LREjJgcuLMxT3um4sZfy9LiUhGErHjxLRaNcaDVGSsmWeiNuhidXg==",
+ "dev": true,
+ "requires": {
+ "@ts-graphviz/common": "^2.1.4"
+ }
+ },
+ "@ts-graphviz/ast": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/ast/-/ast-2.0.5.tgz",
+ "integrity": "sha512-HVT+Bn/smDzmKNJFccwgrpJaEUMPzXQ8d84JcNugzTHNUVgxAIe2Vbf4ug351YJpowivQp6/N7XCluQMjtgi5w==",
+ "dev": true,
+ "requires": {
+ "@ts-graphviz/common": "^2.1.4"
+ }
+ },
+ "@ts-graphviz/common": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/common/-/common-2.1.4.tgz",
+ "integrity": "sha512-PNEzOgE4vgvorp/a4Ev26jVNtiX200yODoyPa8r6GfpPZbxWKW6bdXF6xWqzMkQoO1CnJOYJx2VANDbGqCqCCw==",
+ "dev": true
+ },
+ "@ts-graphviz/core": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ts-graphviz/core/-/core-2.0.5.tgz",
+ "integrity": "sha512-YwaCGAG3Hs0nhxl+2lVuwuTTAK3GO2XHqOGvGIwXQB16nV858rrR5w2YmWCw9nhd11uLTStxLsCAhI9koWBqDA==",
+ "dev": true,
+ "requires": {
+ "@ts-graphviz/ast": "^2.0.5",
+ "@ts-graphviz/common": "^2.1.4"
+ }
+ },
+ "@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "optional": true,
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "@types/body-parser": {
+ "version": "1.19.5",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
+ "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/busboy": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.3.tgz",
+ "integrity": "sha512-YMBLFN/xBD8bnqywIlGyYqsNFXu6bsiY7h3Ae0kO17qEuTjsqeyYMRPSUDacIKIquws2Y6KjmxAyNx8xB3xQbw==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/caseless": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
+ "optional": true
+ },
+ "@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true
+ },
+ "@types/express": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
+ "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.43",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz",
+ "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==",
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "@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==",
+ "dev": true
+ },
+ "@types/http-errors": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
+ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="
+ },
+ "@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "@types/jsonwebtoken": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz",
+ "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true
+ },
+ "@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
+ "@types/markdown-it": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==",
+ "dev": true,
+ "requires": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true
+ },
+ "@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
+ },
+ "@types/node": {
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
+ "requires": {
+ "undici-types": "~6.19.8"
+ }
+ },
+ "@types/normalize-package-data": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+ "dev": true
+ },
+ "@types/object-path": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.4.tgz",
+ "integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw=="
+ },
+ "@types/qs": {
+ "version": "6.9.11",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
+ "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ=="
+ },
+ "@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
+ },
+ "@types/request": {
+ "version": "2.48.13",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
+ "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
+ "optional": true,
+ "requires": {
+ "@types/caseless": "*",
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "form-data": "^2.5.5"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
+ "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
+ "optional": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.35",
+ "safe-buffer": "^5.2.1"
+ }
+ }
+ }
+ },
+ "@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+ "dev": true
+ },
+ "@types/send": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
+ "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
+ "requires": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "@types/serve-static": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
+ "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
+ "requires": {
+ "@types/http-errors": "*",
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/tough-cookie": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+ "optional": true
+ },
+ "@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
+ },
+ "@types/webidl-conversions": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
+ },
+ "@types/whatwg-url": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
+ "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
+ "dev": true,
+ "requires": {
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
+ "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/type-utils": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true
+ },
+ "ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
+ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.5"
+ }
+ },
+ "ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "@typescript-eslint/project-service": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
+ "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/tsconfig-utils": "^8.58.0",
+ "@typescript-eslint/types": "^8.58.0",
+ "debug": "^4.4.3"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
+ "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
+ "dev": true
+ }
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
+ "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ }
+ }
+ },
+ "@typescript-eslint/tsconfig-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
+ "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
+ "dev": true,
+ "requires": {}
+ },
+ "@typescript-eslint/type-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
+ "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.5"
+ }
+ },
+ "ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
+ "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.5"
+ }
+ },
+ "ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "7.18.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "@vue/compiler-core": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz",
+ "integrity": "sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.25.3",
+ "@vue/shared": "3.5.11",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "@vue/compiler-dom": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz",
+ "integrity": "sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==",
+ "dev": true,
+ "requires": {
+ "@vue/compiler-core": "3.5.11",
+ "@vue/shared": "3.5.11"
+ }
+ },
+ "@vue/compiler-sfc": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.11.tgz",
+ "integrity": "sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.25.3",
+ "@vue/compiler-core": "3.5.11",
+ "@vue/compiler-dom": "3.5.11",
+ "@vue/compiler-ssr": "3.5.11",
+ "@vue/shared": "3.5.11",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.11",
+ "postcss": "^8.4.47",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "@vue/compiler-ssr": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.11.tgz",
+ "integrity": "sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==",
+ "dev": true,
+ "requires": {
+ "@vue/compiler-dom": "3.5.11",
+ "@vue/shared": "3.5.11"
+ }
+ },
+ "@vue/shared": {
+ "version": "3.5.11",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz",
+ "integrity": "sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==",
+ "dev": true
+ },
+ "@whatwg-node/promise-helpers": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
+ "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
+ "requires": {
+ "tslib": "^2.6.3"
+ }
+ },
+ "@wry/caches": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
+ "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@wry/context": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
+ "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@wry/equality": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
+ "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "@wry/trie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
+ "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
+ "abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "optional": true,
+ "requires": {
+ "event-target-shim": "^5.0.0"
+ }
+ },
+ "abstract-logging": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
+ },
+ "accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "requires": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
+ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="
+ },
+ "mime-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
+ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
+ "requires": {
+ "mime-db": "^1.53.0"
+ }
+ }
+ }
+ },
+ "acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "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": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="
+ },
+ "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"
+ }
+ },
+ "all-node-versions": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/all-node-versions/-/all-node-versions-13.0.1.tgz",
+ "integrity": "sha512-5pG14FNgn5ClyGv8diB7uTcsmi2NWk9rDH+cGbVsqHjeqptegK0UfCsBA/vNUOZPNOPnYNzk31EM9OjJktld/g==",
+ "dev": true,
+ "requires": {
+ "fetch-node-website": "^9.0.1",
+ "filter-obj": "^6.1.0",
+ "global-cache-dir": "^6.0.1",
+ "is-plain-obj": "^4.1.0",
+ "path-exists": "^5.0.0",
+ "semver": "^7.7.1",
+ "write-file-atomic": "^6.0.0"
+ },
+ "dependencies": {
+ "path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "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
+ },
+ "write-file-atomic": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
+ "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ }
+ }
+ }
+ },
+ "ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "requires": {
+ "environment": "^1.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "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"
+ }
+ },
+ "ansicolors": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+ "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
+ "dev": true
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true
+ },
+ "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,
+ "optional": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "apollo-upload-client": {
+ "version": "18.0.1",
+ "resolved": "https://registry.npmjs.org/apollo-upload-client/-/apollo-upload-client-18.0.1.tgz",
+ "integrity": "sha512-OQvZg1rK05VNI79D658FUmMdoI2oB/KJKb6QGMa2Si25QXOaAvLMBFUEwJct7wf+19U8vk9ILhidBOU1ZWv6QA==",
+ "dev": true,
+ "requires": {
+ "extract-files": "^13.0.0"
+ }
+ },
+ "app-module-path": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+ "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==",
+ "dev": true
+ },
+ "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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+ },
+ "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
+ },
+ "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
+ },
+ "argv-formatter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz",
+ "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==",
+ "dev": true
+ },
+ "array-ify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "optional": true
+ },
+ "asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "assert-options": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.3.tgz",
+ "integrity": "sha512-s6v4HnA+vYSGO4eZX+F+I3gvF74wPk+m6Z1Q3w1Dsg4Pnv/R24vhKAasoMVZGvDpOOfTg1Qz4ptZnEbuy95XsQ=="
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
+ },
+ "ast-module-types": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-6.0.0.tgz",
+ "integrity": "sha512-LFRg7178Fw5R4FAEwZxVqiRI8IxSM+Ay2UBrHoCerXNme+kMMMfz7T3xDGV/c2fer87hcrtgJGsnSOfUrPK6ng==",
+ "dev": true
+ },
+ "async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
+ },
+ "async-retry": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
+ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+ "requires": {
+ "retry": "0.13.1"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "devOptional": true
+ },
+ "available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "requires": {
+ "possible-typed-array-names": "^1.0.0"
+ }
+ },
+ "babel-plugin-polyfill-corejs2": {
+ "version": "0.4.17",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
+ "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "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-plugin-polyfill-corejs3": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
+ "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "core-js-compat": "^3.48.0"
+ }
+ },
+ "babel-plugin-polyfill-regenerator": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
+ "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8"
+ }
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "backoff": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
+ "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
+ "requires": {
+ "precond": "0.2"
+ }
+ },
+ "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
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+ },
+ "baseline-browser-mapping": {
+ "version": "2.10.12",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz",
+ "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==",
+ "dev": true
+ },
+ "bcryptjs": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
+ "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="
+ },
+ "before-after-hook": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
+ "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
+ "dev": true
+ },
+ "bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "optional": true
+ },
+ "bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "bn.js": {
+ "version": "4.12.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz",
+ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="
+ },
+ "body-parser": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+ "requires": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.1",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
+ }
+ },
+ "bottleneck": {
+ "version": "2.19.5",
+ "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
+ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.1.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "requires": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ }
+ },
+ "bson": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-7.1.1.tgz",
+ "integrity": "sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw=="
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true
+ },
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
+ "dev": true
+ },
+ "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
+ },
+ "busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "requires": {
+ "streamsearch": "^1.1.0"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ },
+ "cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "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"
+ }
+ },
+ "cachedir": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+ "dev": true
+ },
+ "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"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ }
+ },
+ "call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ }
+ },
+ "call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dev": true,
+ "requires": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001782",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001782.tgz",
+ "integrity": "sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw==",
+ "dev": true
+ },
+ "cardinal": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz",
+ "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==",
+ "dev": true,
+ "requires": {
+ "ansicolors": "~0.3.2",
+ "redeyed": "~2.1.0"
+ }
+ },
+ "catharsis": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "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"
+ }
+ },
+ "char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "optional": 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"
+ }
+ },
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true
+ },
+ "clean-css": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+ "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.0"
+ }
+ },
+ "clean-jsdoc-theme": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.3.0.tgz",
+ "integrity": "sha512-QMrBdZ2KdPt6V2Ytg7dIt0/q32U4COpxvR0UDhPjRRKRL0o0MvRCR5YpY37/4rPF1SI1AYEKAWyof7ndCb/dzA==",
+ "dev": true,
+ "requires": {
+ "@jsdoc/salty": "^0.2.4",
+ "fs-extra": "^10.1.0",
+ "html-minifier-terser": "^7.2.0",
+ "klaw-sync": "^6.0.0",
+ "lodash": "^4.17.21",
+ "showdown": "^2.1.0"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-highlight": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
+ "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0",
+ "highlight.js": "^10.7.1",
+ "mz": "^2.4.0",
+ "parse5": "^5.1.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.0",
+ "yargs": "^16.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "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
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "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"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true
+ }
+ }
+ },
+ "cli-progress": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.3"
+ }
+ },
+ "cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "dev": true
+ },
+ "cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+ "dev": true,
+ "requires": {
+ "@colors/colors": "1.5.0",
+ "string-width": "^4.2.0"
+ }
+ },
+ "cli-truncate": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
+ "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
+ "dev": true,
+ "requires": {
+ "slice-ansi": "^8.0.0",
+ "string-width": "^8.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
+ "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
+ "dev": true,
+ "requires": {
+ "get-east-asian-width": "^1.5.0",
+ "strip-ansi": "^7.1.2"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.2.2"
+ }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true
+ },
+ "cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="
+ },
+ "color": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
+ "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
+ "requires": {
+ "color-convert": "^3.1.3",
+ "color-string": "^2.1.3"
+ },
+ "dependencies": {
+ "color-convert": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
+ "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
+ "requires": {
+ "color-name": "^2.0.0"
+ }
+ },
+ "color-name": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="
+ }
+ }
+ },
+ "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
+ },
+ "color-string": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
+ "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
+ "requires": {
+ "color-name": "^2.0.0"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="
+ }
+ }
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+ },
+ "colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ },
+ "colors-option": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/colors-option/-/colors-option-6.0.1.tgz",
+ "integrity": "sha512-FsAlu5KTTN+W6Xc4NpxNAhl8iCKwVBzjL7Y2ZK6G9zMv50AfMDlU7Mi16lzaDK8Iwpoq/GfAXX+WrYx38gfSHA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^5.4.1",
+ "is-plain-obj": "^4.1.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+ "dev": true
+ }
+ }
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "devOptional": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "compare-func": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+ "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+ "dev": true,
+ "requires": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "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=="
+ },
+ "content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "requires": {
+ "safe-buffer": "5.2.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
+ },
+ "conventional-changelog-angular": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz",
+ "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==",
+ "dev": true,
+ "requires": {
+ "compare-func": "^2.0.0"
+ }
+ },
+ "conventional-changelog-writer": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz",
+ "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==",
+ "dev": true,
+ "requires": {
+ "@types/semver": "^7.5.5",
+ "conventional-commits-filter": "^5.0.0",
+ "handlebars": "^4.7.7",
+ "meow": "^13.0.0",
+ "semver": "^7.5.2"
+ }
+ },
+ "conventional-commits-filter": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz",
+ "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==",
+ "dev": true
+ },
+ "conventional-commits-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz",
+ "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==",
+ "dev": true,
+ "requires": {
+ "meow": "^13.0.0"
+ }
+ },
+ "convert-hrtime": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz",
+ "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==",
+ "dev": 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
+ },
+ "cookie": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="
+ },
+ "cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="
+ },
+ "core-js-compat": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz",
+ "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.28.1"
+ }
+ },
+ "core-js-pure": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz",
+ "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw=="
+ },
+ "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==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ }
+ },
+ "cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.1"
+ }
+ },
+ "cross-inspect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
+ "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "devOptional": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+ },
+ "crypto-random-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz",
+ "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^1.0.1"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "dev": true
+ }
+ }
+ },
+ "data-uri-to-buffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
+ "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
+ },
+ "debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "requires": {
+ "ms": "^2.1.3"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true
+ },
+ "decompress": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz",
+ "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==",
+ "dev": true,
+ "requires": {
+ "decompress-tar": "^4.0.0",
+ "decompress-tarbz2": "^4.0.0",
+ "decompress-targz": "^4.0.0",
+ "decompress-unzip": "^4.0.1",
+ "graceful-fs": "^4.1.10",
+ "make-dir": "^1.0.0",
+ "pify": "^2.3.0",
+ "strip-dirs": "^2.0.0"
+ },
+ "dependencies": {
+ "make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true
+ }
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": 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==",
+ "dev": true,
+ "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==",
+ "dev": true
+ }
+ }
+ },
+ "decompress-tar": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz",
+ "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==",
+ "dev": true,
+ "requires": {
+ "file-type": "^5.2.0",
+ "is-stream": "^1.1.0",
+ "tar-stream": "^1.5.2"
+ },
+ "dependencies": {
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true
+ }
+ }
+ },
+ "decompress-tarbz2": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz",
+ "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==",
+ "dev": true,
+ "requires": {
+ "decompress-tar": "^4.1.0",
+ "file-type": "^6.1.0",
+ "is-stream": "^1.1.0",
+ "seek-bzip": "^1.0.5",
+ "unbzip2-stream": "^1.0.9"
+ },
+ "dependencies": {
+ "file-type": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz",
+ "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true
+ }
+ }
+ },
+ "decompress-targz": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz",
+ "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==",
+ "dev": true,
+ "requires": {
+ "decompress-tar": "^4.1.1",
+ "file-type": "^5.2.0",
+ "is-stream": "^1.1.0"
+ },
+ "dependencies": {
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true
+ }
+ }
+ },
+ "decompress-unzip": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
+ "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==",
+ "dev": true,
+ "requires": {
+ "file-type": "^3.8.0",
+ "get-stream": "^2.2.0",
+ "pify": "^2.3.0",
+ "yauzl": "^2.4.2"
+ },
+ "dependencies": {
+ "file-type": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+ "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
+ "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true
+ }
+ }
+ },
+ "deep-diff": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz",
+ "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==",
+ "dev": true
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.2"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "devOptional": true
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ },
+ "dependency-tree": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-11.0.1.tgz",
+ "integrity": "sha512-eCt7HSKIC9NxgIykG2DRq3Aewn9UhVS14MB3rEn6l/AsEI1FBg6ZGSlCU0SZ6Tjm2kkhj6/8c2pViinuyKELhg==",
+ "dev": true,
+ "requires": {
+ "commander": "^12.0.0",
+ "filing-cabinet": "^5.0.1",
+ "precinct": "^12.0.2",
+ "typescript": "^5.4.5"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "deprecation": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
+ "dev": true
+ },
+ "detective-amd": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-6.0.0.tgz",
+ "integrity": "sha512-NTqfYfwNsW7AQltKSEaWR66hGkTeD52Kz3eRQ+nfkA9ZFZt3iifRCWh+yZ/m6t3H42JFwVFTrml/D64R2PAIOA==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^6.0.0",
+ "escodegen": "^2.1.0",
+ "get-amd-module-type": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-cjs": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-6.0.0.tgz",
+ "integrity": "sha512-R55jTS6Kkmy6ukdrbzY4x+I7KkXiuDPpFzUViFV/tm2PBGtTCjkh9ZmTuJc1SaziMHJOe636dtiZLEuzBL9drg==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-es6": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-5.0.0.tgz",
+ "integrity": "sha512-NGTnzjvgeMW1khUSEXCzPDoraLenWbUjCFjwxReH+Ir+P6LGjYtaBbAvITWn2H0VSC+eM7/9LFOTAkrta6hNYg==",
+ "dev": true,
+ "requires": {
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-postcss": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-7.0.0.tgz",
+ "integrity": "sha512-pSXA6dyqmBPBuERpoOKKTUUjQCZwZPLRbd1VdsTbt6W+m/+6ROl4BbE87yQBUtLoK7yX8pvXHdKyM/xNIW9F7A==",
+ "dev": true,
+ "requires": {
+ "is-url": "^1.2.4",
+ "postcss-values-parser": "^6.0.2"
+ }
+ },
+ "detective-sass": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-6.0.0.tgz",
+ "integrity": "sha512-h5GCfFMkPm4ZUUfGHVPKNHKT8jV7cSmgK+s4dgQH4/dIUNh9/huR1fjEQrblOQNDalSU7k7g+tiW9LJ+nVEUhg==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-scss": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-5.0.0.tgz",
+ "integrity": "sha512-Y64HyMqntdsCh1qAH7ci95dk0nnpA29g319w/5d/oYcHolcGUVJbIhOirOFjfN1KnMAXAFm5FIkZ4l2EKFGgxg==",
+ "dev": true,
+ "requires": {
+ "gonzales-pe": "^4.3.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-stylus": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-5.0.0.tgz",
+ "integrity": "sha512-KMHOsPY6aq3196WteVhkY5FF+6Nnc/r7q741E+Gq+Ax9mhE2iwj8Hlw8pl+749hPDRDBHZ2WlgOjP+twIG61vQ==",
+ "dev": true
+ },
+ "detective-typescript": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-13.0.0.tgz",
+ "integrity": "sha512-tcMYfiFWoUejSbvSblw90NDt76/4mNftYCX0SMnVRYzSXv8Fvo06hi4JOPdNvVNxRtCAKg3MJ3cBJh+ygEMH+A==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "^7.6.0",
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "detective-vue2": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/detective-vue2/-/detective-vue2-2.0.3.tgz",
+ "integrity": "sha512-AgWdSfVnft8uPGnUkdvE1EDadEENDCzoSRMt2xZfpxsjqVO617zGWXbB8TGIxHaqHz/nHa6lOSgAB8/dt0yEug==",
+ "dev": true,
+ "requires": {
+ "@vue/compiler-sfc": "^3.4.27",
+ "detective-es6": "^5.0.0",
+ "detective-sass": "^6.0.0",
+ "detective-scss": "^5.0.0",
+ "detective-stylus": "^5.0.0",
+ "detective-typescript": "^13.0.0"
+ }
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
+ "dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ }
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "duplexify": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+ "optional": true,
+ "requires": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "optional": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "devOptional": true
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "electron-to-chromium": {
+ "version": "1.5.328",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz",
+ "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==",
+ "dev": true
+ },
+ "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=="
+ },
+ "emojilib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
+ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
+ "dev": true
+ },
+ "enabled": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
+ },
+ "encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "devOptional": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true
+ },
+ "env-ci": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.2.0.tgz",
+ "integrity": "sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==",
+ "dev": true,
+ "requires": {
+ "execa": "^8.0.0",
+ "java-properties": "^1.0.2"
+ },
+ "dependencies": {
+ "execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true
+ },
+ "human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ }
+ },
+ "onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^4.0.0"
+ }
+ },
+ "path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "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
+ },
+ "strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true
+ }
+ }
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true
+ },
+ "environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true
+ },
+ "err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+ },
+ "es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "requires": {
+ "es-errors": "^1.3.0"
+ }
+ },
+ "es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "devOptional": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ }
+ },
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "devOptional": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "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
+ },
+ "escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ }
+ }
+ },
+ "eslint": {
+ "version": "9.27.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
+ "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.14.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.27.0",
+ "@eslint/plugin-kit": "^0.3.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "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-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "eslint-plugin-expect-type": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-expect-type/-/eslint-plugin-expect-type-0.6.2.tgz",
+ "integrity": "sha512-XWgtpplzr6GlpPUFG9ZApnSTv7QJXAPNN6hNmrlleVVCkAK23f/3E2BiCoA3Xtb0rIKfVKh7TLe+D1tcGt8/1w==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/utils": "^6.10.0 || ^7.0.1 || ^8",
+ "fs-extra": "^11.1.1",
+ "get-tsconfig": "^4.8.1"
+ }
+ },
+ "eslint-plugin-unused-imports": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz",
+ "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true
+ },
+ "espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true
+ }
+ }
+ },
+ "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.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ }
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "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
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+ },
+ "event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "optional": true
+ },
+ "eventemitter3": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "expo-server-sdk": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-6.1.0.tgz",
+ "integrity": "sha512-ISuax1AQ7cpM5RAqcu8gVcoLL0ZKskJ5OLoMWmdITBe9nYjTucjdGyBq817YkIvTcj1pAUwx+9toUT7l/V7thA==",
+ "requires": {
+ "promise-limit": "^2.7.0",
+ "promise-retry": "^2.0.1",
+ "undici": "^7.2.0"
+ },
+ "dependencies": {
+ "undici": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz",
+ "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ=="
+ }
+ }
+ },
+ "express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "requires": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
+ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="
+ },
+ "mime-types": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
+ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
+ "requires": {
+ "mime-db": "^1.53.0"
+ }
+ }
+ }
+ },
+ "express-rate-limit": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
+ "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
+ "requires": {
+ "ip-address": "10.1.0"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
+ "extract-files": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-13.0.0.tgz",
+ "integrity": "sha512-FXD+2Tsr8Iqtm3QZy1Zmwscca7Jx3mMC5Crr+sEP1I303Jy1CYMuYCm7hRTplFNg3XdUavErkxnTzpaqdSoi6g==",
+ "dev": true,
+ "requires": {
+ "is-plain-obj": "^4.1.0"
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
+ },
+ "farmhash-modern": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz",
+ "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA=="
+ },
+ "fast-content-type-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
+ "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
+ "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=="
+ },
+ "fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "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
+ },
+ "fast-xml-builder": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
+ "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
+ "optional": true,
+ "requires": {
+ "path-expression-matcher": "^1.1.3"
+ }
+ },
+ "fast-xml-parser": {
+ "version": "5.5.9",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz",
+ "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==",
+ "optional": true,
+ "requires": {
+ "fast-xml-builder": "^1.1.4",
+ "path-expression-matcher": "^1.2.0",
+ "strnum": "^2.2.2"
+ }
+ },
+ "fastq": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
+ "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
+ },
+ "fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "requires": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ }
+ },
+ "fetch-node-website": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/fetch-node-website/-/fetch-node-website-9.0.1.tgz",
+ "integrity": "sha512-htQY+YRRFdMAxmQG8EpnVy32lQyXBjgFAvyfaaq7VCn53Py1gorggPMYAt1Zmp0AlNS1X/YnGt641RAkUbsETw==",
+ "dev": true,
+ "requires": {
+ "cli-progress": "^3.12.0",
+ "colors-option": "^6.0.1",
+ "figures": "^6.0.1",
+ "got": "^13.0.0",
+ "is-plain-obj": "^4.1.0"
+ }
+ },
+ "figures": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
+ "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
+ "dev": true,
+ "requires": {
+ "is-unicode-supported": "^2.0.0"
+ },
+ "dependencies": {
+ "is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "dev": true
+ }
+ }
+ },
+ "file-entry-cache": {
+ "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,
+ "requires": {
+ "flat-cache": "^4.0.0"
+ }
+ },
+ "file-stream-rotator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
+ "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
+ "requires": {
+ "moment": "^2.29.1"
+ }
+ },
+ "file-type": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
+ "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==",
+ "dev": true
+ },
+ "filing-cabinet": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.0.2.tgz",
+ "integrity": "sha512-RZlFj8lzyu6jqtFBeXNqUjjNG6xm+gwXue3T70pRxw1W40kJwlgq0PSWAmh0nAnn5DHuBIecLXk9+1VKS9ICXA==",
+ "dev": true,
+ "requires": {
+ "app-module-path": "^2.2.0",
+ "commander": "^12.0.0",
+ "enhanced-resolve": "^5.16.0",
+ "module-definition": "^6.0.0",
+ "module-lookup-amd": "^9.0.1",
+ "resolve": "^1.22.8",
+ "resolve-dependency-path": "^4.0.0",
+ "sass-lookup": "^6.0.1",
+ "stylus-lookup": "^6.0.0",
+ "tsconfig-paths": "^4.2.0",
+ "typescript": "^5.4.4"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "filter-obj": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-6.1.0.tgz",
+ "integrity": "sha512-xdMtCAODmPloU9qtmPcdBV9Kd27NtMse+4ayThxqIHUES5Z2S6bGpap5PpdmNM56ub7y3i1eyr+vJJIIgWGKmA==",
+ "dev": true
+ },
+ "finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "requires": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ }
+ },
+ "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"
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.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"
+ }
+ },
+ "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"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "find-up-simple": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
+ "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
+ "dev": true
+ },
+ "find-versions": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz",
+ "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==",
+ "dev": true,
+ "requires": {
+ "semver-regex": "^4.0.5",
+ "super-regex": "^1.0.0"
+ }
+ },
+ "firebase-admin": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.7.0.tgz",
+ "integrity": "sha512-o3qS8zCJbApe7aKzkO2Pa380t9cHISqeSd3blqYTtOuUUUua3qZTLwNWgGUOss3td6wbzrZhiHIj3c8+fC046Q==",
+ "requires": {
+ "@fastify/busboy": "^3.0.0",
+ "@firebase/database-compat": "^2.0.0",
+ "@firebase/database-types": "^1.0.6",
+ "@google-cloud/firestore": "^7.11.0",
+ "@google-cloud/storage": "^7.19.0",
+ "farmhash-modern": "^1.1.0",
+ "fast-deep-equal": "^3.1.1",
+ "google-auth-library": "^10.6.1",
+ "jsonwebtoken": "^9.0.0",
+ "jwks-rsa": "^3.1.0",
+ "node-forge": "^1.3.1",
+ "uuid": "^11.0.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="
+ }
+ }
+ },
+ "flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ }
+ },
+ "flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true
+ },
+ "fn.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
+ },
+ "follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="
+ },
+ "for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "requires": {
+ "is-callable": "^1.2.7"
+ }
+ },
+ "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"
+ }
+ },
+ "form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "requires": {
+ "fetch-blob": "^3.1.2"
+ }
+ },
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+ },
+ "fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="
+ },
+ "from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "fromentries": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
+ "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
+ "dev": true
+ },
+ "fs-capacitor": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz",
+ "integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw=="
+ },
+ "fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": 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==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "fs-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": 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=="
+ },
+ "function-timeout": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz",
+ "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+ "optional": true
+ },
+ "gauge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-5.0.1.tgz",
+ "integrity": "sha512-CmykPMJGuNan/3S4kZOpvvPYSNqSHANiWnh9XcMU2pSjtBfF0XzZ2p1bFAxTbnFxyBuPxQYHhzwaoOmUdqzvxQ==",
+ "requires": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^4.0.1",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ },
+ "dependencies": {
+ "signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
+ }
+ }
+ },
+ "gaxios": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
+ "optional": true,
+ "requires": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9",
+ "uuid": "^9.0.1"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "optional": true
+ },
+ "uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "optional": true
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "gcp-metadata": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz",
+ "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ }
+ },
+ "glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "optional": true,
+ "peer": true
+ },
+ "rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "glob": "^10.3.7"
+ }
+ },
+ "signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "optional": true,
+ "peer": true
+ }
+ }
+ },
+ "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-amd-module-type": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.0.tgz",
+ "integrity": "sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "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-east-asian-width": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
+ "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "requires": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ }
+ },
+ "get-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": 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-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "requires": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "get-tsconfig": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
+ "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
+ "dev": true,
+ "requires": {
+ "resolve-pkg-maps": "^1.0.0"
+ }
+ },
+ "git-log-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz",
+ "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==",
+ "dev": true,
+ "requires": {
+ "argv-formatter": "~1.0.0",
+ "spawn-error-forwarder": "~1.0.0",
+ "split2": "~1.0.0",
+ "stream-combiner2": "~1.1.1",
+ "through2": "~2.0.0",
+ "traverse": "~0.6.6"
+ },
+ "dependencies": {
+ "split2": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz",
+ "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==",
+ "dev": true,
+ "requires": {
+ "through2": "~2.0.0"
+ }
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "global-cache-dir": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/global-cache-dir/-/global-cache-dir-6.0.1.tgz",
+ "integrity": "sha512-HOOgvCW8le14HM0sTTvyYkTMRot7hq5ERIzNTUcDyZ4Vr9qF/IHUZeIcz4+v6vpwTFMqZ8QHKJYpXYRy/DSb6A==",
+ "dev": true,
+ "requires": {
+ "cachedir": "^2.4.0",
+ "path-exists": "^5.0.0"
+ },
+ "dependencies": {
+ "path-exists": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+ "dev": true
+ }
+ }
+ },
+ "globals": {
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
+ "dev": true
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "dependencies": {
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ }
+ }
+ },
+ "gonzales-pe": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
+ "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "google-auth-library": {
+ "version": "10.6.2",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz",
+ "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==",
+ "requires": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.1.4",
+ "gcp-metadata": "8.1.2",
+ "google-logging-utils": "1.1.3",
+ "jws": "^4.0.0"
+ },
+ "dependencies": {
+ "gaxios": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz",
+ "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==",
+ "requires": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
+ }
+ },
+ "gcp-metadata": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+ "requires": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ }
+ }
+ }
+ },
+ "google-gax": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz",
+ "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==",
+ "optional": true,
+ "requires": {
+ "@grpc/grpc-js": "^1.10.9",
+ "@grpc/proto-loader": "^0.7.13",
+ "@types/long": "^4.0.0",
+ "abort-controller": "^3.0.0",
+ "duplexify": "^4.0.0",
+ "google-auth-library": "^9.3.0",
+ "node-fetch": "^2.7.0",
+ "object-hash": "^3.0.0",
+ "proto3-json-serializer": "^2.0.2",
+ "protobufjs": "^7.3.2",
+ "retry-request": "^7.0.0",
+ "uuid": "^9.0.1"
+ },
+ "dependencies": {
+ "gcp-metadata": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+ "optional": true,
+ "requires": {
+ "gaxios": "^6.1.1",
+ "google-logging-utils": "^0.0.2",
+ "json-bigint": "^1.0.0"
+ }
+ },
+ "google-auth-library": {
+ "version": "9.15.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "optional": true,
+ "requires": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^6.1.1",
+ "gcp-metadata": "^6.1.0",
+ "gtoken": "^7.0.0",
+ "jws": "^4.0.0"
+ }
+ },
+ "google-logging-utils": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+ "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"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "optional": true
+ },
+ "uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "optional": true
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="
+ },
+ "gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
+ },
+ "got": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
+ "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
+ "dev": true,
+ "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.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "graphql": {
+ "version": "16.13.2",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz",
+ "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig=="
+ },
+ "graphql-list-fields": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/graphql-list-fields/-/graphql-list-fields-2.0.4.tgz",
+ "integrity": "sha512-q3prnhAL/dBsD+vaGr83B8DzkBijg+Yh+lbt7qp2dW1fpuO+q/upzDXvFJstVsSAA8m11MHGkSxxyxXeLou4MA=="
+ },
+ "graphql-relay": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.10.2.tgz",
+ "integrity": "sha512-abybva1hmlNt7Y9pMpAzHuFnM2Mme/a2Usd8S4X27fNteLGRAECMYfhmsrpZFvGn3BhmBZugMXYW/Mesv3P1Kw==",
+ "requires": {}
+ },
+ "graphql-tag": {
+ "version": "2.12.6",
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+ "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "graphql-upload": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-15.0.2.tgz",
+ "integrity": "sha512-ufJAkZJBKWRDD/4wJR3VZMy9QWTwqIYIciPtCEF5fCNgWF+V1p7uIgz+bP2YYLiS4OJBhCKR8rnqE/Wg3XPUiw==",
+ "requires": {
+ "@types/busboy": "^1.5.0",
+ "@types/node": "*",
+ "@types/object-path": "^0.11.1",
+ "busboy": "^1.6.0",
+ "fs-capacitor": "^6.2.0",
+ "http-errors": "^2.0.0",
+ "object-path": "^0.11.8"
+ }
+ },
+ "gtoken": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+ "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
+ "optional": true,
+ "requires": {
+ "gaxios": "^6.0.0",
+ "jws": "^4.0.0"
+ }
+ },
+ "handlebars": {
+ "version": "4.7.9",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
+ "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ }
+ },
+ "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
+ },
+ "has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "requires": {
+ "es-define-property": "^1.0.0"
+ }
+ },
+ "has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
+ },
+ "has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "requires": {
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+ },
+ "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==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
+ "highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "dev": true
+ },
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dev": true,
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "hook-std": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
+ "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
+ "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^10.0.1"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true
+ }
+ }
+ },
+ "html-entities": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
+ "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
+ "optional": 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-minifier-terser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+ "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+ "dev": true,
+ "requires": {
+ "camel-case": "^4.1.2",
+ "clean-css": "~5.3.2",
+ "commander": "^10.0.0",
+ "entities": "^4.4.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.15.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true
+ }
+ }
+ },
+ "http_ece": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
+ "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA=="
+ },
+ "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==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA=="
+ },
+ "http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "http2-wrapper": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+ "dev": true,
+ "requires": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "requires": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ }
+ },
+ "human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+ "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "idb-keyval": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+ "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg=="
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "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"
+ }
+ },
+ "import-from-esm": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz",
+ "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ }
+ },
+ "import-meta-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+ "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
+ "dev": true
+ },
+ "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
+ },
+ "index-to-position": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz",
+ "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==",
+ "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==",
+ "dev": 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=="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "intersect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz",
+ "integrity": "sha512-qsc720yevCO+4NydrJWgEWKccAQwTOvj2m73O/VBA6iUL2HGZJ9XqBiyraNrBXX/W1IAjdpXdRZk24sq8TzBRg=="
+ },
+ "into-stream": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz",
+ "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==",
+ "dev": true,
+ "requires": {
+ "from2": "^2.3.0",
+ "p-is-promise": "^3.0.0"
+ }
+ },
+ "ip-address": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "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,
+ "optional": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
+ },
+ "is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "requires": {
+ "hasown": "^2.0.2"
+ }
+ },
+ "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=="
+ },
+ "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-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true
+ },
+ "is-natural-number": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
+ "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==",
+ "dev": true
+ },
+ "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-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true
+ },
+ "is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "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=="
+ },
+ "is-text-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+ "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+ "dev": true,
+ "requires": {
+ "text-extensions": "^2.0.0"
+ }
+ },
+ "is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "requires": {
+ "which-typed-array": "^1.1.16"
+ }
+ },
+ "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-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "dev": true
+ },
+ "is-url-superb": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz",
+ "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
+ "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==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "devOptional": true
+ },
+ "issue-parser": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz",
+ "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==",
+ "dev": true,
+ "requires": {
+ "lodash.capitalize": "^4.2.1",
+ "lodash.escaperegexp": "^4.1.2",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.uniqby": "^4.7.0"
+ }
+ },
+ "istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+ "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
+ "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": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ }
+ },
+ "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": {
+ "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"
+ }
+ },
+ "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.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "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
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "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"
+ }
+ }
+ }
+ },
+ "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.5",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
+ "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ }
+ },
+ "iterall": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz",
+ "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "devOptional": true,
+ "requires": {
+ "@isaacs/cliui": "^8.0.2",
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "jasmine": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-6.1.0.tgz",
+ "integrity": "sha512-WPphPqEMY0uBRMjuhRHoVoxQNvJuxIMqz0yIcJ3k3oYxBedeGoH60/NXNgasxnx2FvfXrq5/r+2wssJ7WE8ABw==",
+ "dev": true,
+ "requires": {
+ "@jasminejs/reporters": "^1.0.0",
+ "glob": "^10.2.2 || ^11.0.3 || ^12.0.0 || ^13.0.0",
+ "jasmine-core": "~6.1.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "requires": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "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
+ }
+ }
+ },
+ "jasmine-core": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.1.0.tgz",
+ "integrity": "sha512-p/tjBw58O6vxKIWMlrU+yys8lqR3+l3UrqwNTT7wpj+dQ7N4etQekFM8joI+cWzPDYqZf54kN+hLC1+s5TvZvg==",
+ "dev": true
+ },
+ "jasmine-spec-reporter": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz",
+ "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==",
+ "dev": true,
+ "requires": {
+ "colors": "1.4.0"
+ }
+ },
+ "java-properties": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz",
+ "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==",
+ "dev": true
+ },
+ "jose": {
+ "version": "4.15.5",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz",
+ "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg=="
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "js2xmlparser": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
+ "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
+ "dev": true,
+ "requires": {
+ "xmlcreate": "^2.0.4"
+ }
+ },
+ "jsdoc": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz",
+ "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.20.15",
+ "@jsdoc/salty": "^0.2.1",
+ "@types/markdown-it": "^14.1.1",
+ "bluebird": "^3.7.2",
+ "catharsis": "^0.9.0",
+ "escape-string-regexp": "^2.0.0",
+ "js2xmlparser": "^4.0.2",
+ "klaw": "^3.0.0",
+ "markdown-it": "^14.1.0",
+ "markdown-it-anchor": "^8.6.7",
+ "marked": "^4.0.10",
+ "mkdirp": "^1.0.4",
+ "requizzle": "^0.2.3",
+ "strip-json-comments": "^3.1.0",
+ "underscore": "~1.13.2"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true
+ }
+ }
+ },
+ "jsdoc-babel": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsdoc-babel/-/jsdoc-babel-0.5.0.tgz",
+ "integrity": "sha512-PYfTbc3LNTeR8TpZs2M94NLDWqARq0r9gx3SvuziJfmJS7/AeMKvtj0xjzOX0R/4MOVA7/FqQQK7d6U0iEoztQ==",
+ "dev": true,
+ "requires": {
+ "jsdoc-regex": "^1.0.1",
+ "lodash": "^4.17.10"
+ }
+ },
+ "jsdoc-regex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/jsdoc-regex/-/jsdoc-regex-1.0.1.tgz",
+ "integrity": "sha512-CMFgT3K8GbmChWEfLWe6jlv9x33E8wLPzBjxIlh/eHLMcnDF+TF3CL265ZGBe029o1QdFepwVrQu0WuqqNPncg==",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true
+ },
+ "json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "requires": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "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
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "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
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "requires": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ }
+ },
+ "jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "requires": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jwks-rsa": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz",
+ "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==",
+ "requires": {
+ "@types/express": "^4.17.20",
+ "@types/jsonwebtoken": "^9.0.4",
+ "debug": "^4.3.4",
+ "jose": "^4.15.4",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.2.0"
+ }
+ },
+ "jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "requires": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "klaw": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+ "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
+ "kuler": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
+ },
+ "ldapjs": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz",
+ "integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==",
+ "requires": {
+ "@ldapjs/asn1": "^2.0.0",
+ "@ldapjs/attribute": "^1.0.0",
+ "@ldapjs/change": "^1.0.0",
+ "@ldapjs/controls": "^2.1.0",
+ "@ldapjs/dn": "^1.1.0",
+ "@ldapjs/filter": "^2.1.1",
+ "@ldapjs/messages": "^1.3.0",
+ "@ldapjs/protocol": "^1.2.1",
+ "abstract-logging": "^2.0.1",
+ "assert-plus": "^1.0.0",
+ "backoff": "^2.5.0",
+ "once": "^1.4.0",
+ "vasync": "^2.2.1",
+ "verror": "^1.10.1"
+ }
+ },
+ "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"
+ }
+ },
+ "limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+ },
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "dev": true,
+ "requires": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "lint-staged": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz",
+ "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==",
+ "dev": true,
+ "requires": {
+ "commander": "^14.0.3",
+ "listr2": "^9.0.5",
+ "picomatch": "^4.0.3",
+ "string-argv": "^0.3.2",
+ "tinyexec": "^1.0.4",
+ "yaml": "^2.8.2"
+ },
+ "dependencies": {
+ "picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true
+ }
+ }
+ },
+ "listr2": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
+ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
+ "dev": true,
+ "requires": {
+ "cli-truncate": "^5.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.2.2"
+ }
+ },
+ "wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "dev": true
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true
+ }
+ }
+ },
+ "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": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="
+ },
+ "lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "dev": true
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "optional": true
+ },
+ "lodash.capitalize": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
+ "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==",
+ "dev": true
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "lodash.escaperegexp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
+ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
+ "dev": true
+ },
+ "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.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
+ "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
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
+ },
+ "lodash.uniqby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
+ "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
+ "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"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^5.0.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "requires": {
+ "get-east-asian-width": "^1.3.1"
+ }
+ },
+ "onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "requires": {
+ "mimic-function": "^5.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ }
+ },
+ "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
+ },
+ "slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ }
+ },
+ "string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.2.2"
+ }
+ },
+ "wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ }
+ }
+ },
+ "logform": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+ "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+ "requires": {
+ "@colors/colors": "1.6.0",
+ "@types/triple-beam": "^1.3.2",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ },
+ "dependencies": {
+ "@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="
+ }
+ }
+ },
+ "loglevel": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
+ "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg=="
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "11.2.7",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+ "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="
+ },
+ "lru-memoizer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz",
+ "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==",
+ "requires": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "~4.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+ "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==",
+ "requires": {
+ "pseudomap": "^1.0.1",
+ "yallist": "^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=="
+ }
+ }
+ },
+ "m": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/m/-/m-1.10.0.tgz",
+ "integrity": "sha512-+vXing+uUyCeZZlY2RIteWHSPHgVcFyBoeWrBU5F3ibDt847sVPGHK41GriFP05uMvfHZkhlaAMYEHoQkfksvA==",
+ "dev": true
+ },
+ "madge": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/madge/-/madge-8.0.0.tgz",
+ "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.2",
+ "commander": "^7.2.0",
+ "commondir": "^1.0.1",
+ "debug": "^4.3.4",
+ "dependency-tree": "^11.0.0",
+ "ora": "^5.4.1",
+ "pluralize": "^8.0.0",
+ "pretty-ms": "^7.0.1",
+ "rc": "^1.2.8",
+ "stream-to-array": "^2.3.0",
+ "ts-graphviz": "^2.1.2",
+ "walkdir": "^0.4.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true
+ },
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "magic-string": {
+ "version": "0.30.11",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+ "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ }
+ },
+ "markdown-it-anchor": {
+ "version": "8.6.7",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
+ "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
+ "dev": true,
+ "requires": {}
+ },
+ "marked": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "dev": true
+ },
+ "marked-terminal": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz",
+ "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^7.0.0",
+ "ansi-regex": "^6.1.0",
+ "chalk": "^5.4.1",
+ "cli-highlight": "^2.1.11",
+ "cli-table3": "^0.6.5",
+ "node-emoji": "^2.2.0",
+ "supports-hyperlinks": "^3.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+ "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+ "dev": true
+ }
+ }
+ },
+ "math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
+ },
+ "mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "dev": true
+ },
+ "media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="
+ },
+ "memory-pager": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
+ },
+ "meow": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+ "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
+ "dev": true
+ },
+ "merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="
+ },
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+ "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "devOptional": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "devOptional": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true
+ },
+ "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==",
+ "dev": true
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": 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=="
+ },
+ "minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "devOptional": true
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": 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==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
+ "mock-files-adapter": {
+ "version": "file:spec/dependencies/mock-files-adapter"
+ },
+ "mock-mail-adapter": {
+ "version": "file:spec/dependencies/mock-mail-adapter"
+ },
+ "module-definition": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-6.0.0.tgz",
+ "integrity": "sha512-sEGP5nKEXU7fGSZUML/coJbrO+yQtxcppDAYWRE9ovWsTbFoUHB2qDUx564WUzDaBHXsD46JBbIK5WVTwCyu3w==",
+ "dev": true,
+ "requires": {
+ "ast-module-types": "^6.0.0",
+ "node-source-walk": "^7.0.0"
+ }
+ },
+ "module-lookup-amd": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-9.0.2.tgz",
+ "integrity": "sha512-p7PzSVEWiW9fHRX9oM+V4aV5B2nCVddVNv4DZ/JB6t9GsXY4E+ZVhPpnwUX7bbJyGeeVZqhS8q/JZ/H77IqPFA==",
+ "dev": true,
+ "requires": {
+ "commander": "^12.1.0",
+ "glob": "^7.2.3",
+ "requirejs": "^2.3.7",
+ "requirejs-config-file": "^4.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
+ },
+ "mongodb": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.0.tgz",
+ "integrity": "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg==",
+ "requires": {
+ "@mongodb-js/saslprep": "^1.3.0",
+ "bson": "^7.1.1",
+ "mongodb-connection-string-url": "^7.0.0"
+ },
+ "dependencies": {
+ "@types/whatwg-url": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
+ "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
+ "requires": {
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "mongodb-connection-string-url": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
+ "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
+ "requires": {
+ "@types/whatwg-url": "^13.0.0",
+ "whatwg-url": "^14.1.0"
+ }
+ }
+ }
+ },
+ "mongodb-connection-string-url": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
+ "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
+ "dev": true,
+ "requires": {
+ "@types/whatwg-url": "^11.0.2",
+ "whatwg-url": "^14.1.0 || ^13.0.0"
+ }
+ },
+ "mongodb-download-url": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.6.2.tgz",
+ "integrity": "sha512-89g7A+ktFQ6L3fcjV1ClCj5ftlMSuVy22q76N6vhuzxBdYcD2O0Wxt+i16SQ7BAD1QtOPsGpSQVL4bUtLvY6+w==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.4.0",
+ "minimist": "^1.2.8",
+ "node-fetch": "^2.7.0",
+ "semver": "^7.7.1"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true,
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+ },
+ "mongodb-runner": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.9.3.tgz",
+ "integrity": "sha512-2n2fCyUITi0UrAs0eg/zLSehSVOoWWUsgJleEBh6p1otHaiqMSAMURS6W7PLJvvGxFlnO3tjiDB6T11gjqAkUQ==",
+ "dev": true,
+ "requires": {
+ "@mongodb-js/mongodb-downloader": "^0.4.2",
+ "@mongodb-js/saslprep": "^1.3.0",
+ "debug": "^4.4.0",
+ "mongodb": "^6.9.0",
+ "mongodb-connection-string-url": "^3.0.0",
+ "yargs": "^17.7.2"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "bson": {
+ "version": "6.10.4",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
+ "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true
+ },
+ "gaxios": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
+ "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9"
+ }
+ },
+ "gcp-metadata": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
+ "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "gaxios": "^5.0.0",
+ "json-bigint": "^1.0.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==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "mongodb": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz",
+ "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==",
+ "dev": true,
+ "requires": {
+ "@mongodb-js/saslprep": "^1.3.0",
+ "bson": "^6.10.4",
+ "mongodb-connection-string-url": "^3.0.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "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==",
+ "dev": true,
+ "optional": true,
+ "peer": 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==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "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"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="
+ },
+ "mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": 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
+ },
+ "negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "nerf-dart": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz",
+ "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==",
+ "dev": true
+ },
+ "no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "requires": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node-abort-controller": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
+ "dev": true
+ },
+ "node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
+ },
+ "node-emoji": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
+ "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
+ "dev": true,
+ "requires": {
+ "@sindresorhus/is": "^4.6.0",
+ "char-regex": "^1.0.2",
+ "emojilib": "^2.4.0",
+ "skin-tone": "^2.0.0"
+ },
+ "dependencies": {
+ "@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "dev": true
+ }
+ }
+ },
+ "node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "requires": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ }
+ },
+ "node-forge": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="
+ },
+ "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.36",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
+ "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
+ "dev": true
+ },
+ "node-source-walk": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-7.0.0.tgz",
+ "integrity": "sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.24.4"
+ }
+ },
+ "normalize-package-data": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
+ "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^7.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ }
+ },
+ "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,
+ "optional": true
+ },
+ "normalize-url": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
+ "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
+ "dev": true
+ },
+ "npm": {
+ "version": "10.8.1",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz",
+ "integrity": "sha512-Dp1C6SvSMYQI7YHq/y2l94uvI+59Eqbu1EpuKQHQ8p16txXRuRit5gH3Lnaagk2aXDIjg/Iru9pd05bnneKgdw==",
+ "dev": true,
+ "requires": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/config": "^8.3.3",
+ "@npmcli/fs": "^3.1.1",
+ "@npmcli/map-workspaces": "^3.0.6",
+ "@npmcli/package-json": "^5.1.1",
+ "@npmcli/promise-spawn": "^7.0.2",
+ "@npmcli/redact": "^2.0.0",
+ "@npmcli/run-script": "^8.1.0",
+ "@sigstore/tuf": "^2.3.4",
+ "abbrev": "^2.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^18.0.3",
+ "chalk": "^5.3.0",
+ "ci-info": "^4.0.0",
+ "cli-columns": "^4.0.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^10.4.1",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^7.0.2",
+ "ini": "^4.1.3",
+ "init-package-json": "^6.0.3",
+ "is-cidr": "^5.1.0",
+ "json-parse-even-better-errors": "^3.0.2",
+ "libnpmaccess": "^8.0.6",
+ "libnpmdiff": "^6.1.3",
+ "libnpmexec": "^8.1.2",
+ "libnpmfund": "^5.0.11",
+ "libnpmhook": "^10.0.5",
+ "libnpmorg": "^6.0.6",
+ "libnpmpack": "^7.0.3",
+ "libnpmpublish": "^9.0.9",
+ "libnpmsearch": "^7.0.6",
+ "libnpmteam": "^6.0.5",
+ "libnpmversion": "^6.0.3",
+ "make-fetch-happen": "^13.0.1",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.1",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^10.1.0",
+ "nopt": "^7.2.1",
+ "normalize-package-data": "^6.0.1",
+ "npm-audit-report": "^5.0.0",
+ "npm-install-checks": "^6.3.0",
+ "npm-package-arg": "^11.0.2",
+ "npm-pick-manifest": "^9.0.1",
+ "npm-profile": "^10.0.0",
+ "npm-registry-fetch": "^17.0.1",
+ "npm-user-validate": "^2.0.1",
+ "p-map": "^4.0.0",
+ "pacote": "^18.0.6",
+ "parse-conflict-json": "^3.0.1",
+ "proc-log": "^4.2.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^3.0.1",
+ "semver": "^7.6.2",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^10.0.6",
+ "supports-color": "^9.4.0",
+ "tar": "^6.2.1",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^1.3.0",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^5.0.1",
+ "which": "^4.0.0",
+ "write-file-atomic": "^5.0.1"
+ },
+ "dependencies": {
+ "@isaacs/cliui": {
+ "version": "8.0.2",
+ "bundled": true,
+ "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",
+ "bundled": true,
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "9.2.2",
+ "bundled": true,
+ "dev": true
+ },
+ "string-width": {
+ "version": "5.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/agent": {
+ "version": "2.2.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^10.0.1",
+ "socks-proxy-agent": "^8.0.3"
+ }
+ },
+ "@npmcli/arborist": {
+ "version": "7.5.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^3.1.1",
+ "@npmcli/installed-package-contents": "^2.1.0",
+ "@npmcli/map-workspaces": "^3.0.2",
+ "@npmcli/metavuln-calculator": "^7.1.1",
+ "@npmcli/name-from-folder": "^2.0.0",
+ "@npmcli/node-gyp": "^3.0.0",
+ "@npmcli/package-json": "^5.1.0",
+ "@npmcli/query": "^3.1.0",
+ "@npmcli/redact": "^2.0.0",
+ "@npmcli/run-script": "^8.1.0",
+ "bin-links": "^4.0.4",
+ "cacache": "^18.0.3",
+ "common-ancestor-path": "^1.0.1",
+ "hosted-git-info": "^7.0.2",
+ "json-parse-even-better-errors": "^3.0.2",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^10.2.2",
+ "minimatch": "^9.0.4",
+ "nopt": "^7.2.1",
+ "npm-install-checks": "^6.2.0",
+ "npm-package-arg": "^11.0.2",
+ "npm-pick-manifest": "^9.0.1",
+ "npm-registry-fetch": "^17.0.1",
+ "pacote": "^18.0.6",
+ "parse-conflict-json": "^3.0.0",
+ "proc-log": "^4.2.0",
+ "proggy": "^2.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.7",
+ "ssri": "^10.0.6",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^3.0.1"
+ }
+ },
+ "@npmcli/config": {
+ "version": "8.3.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/map-workspaces": "^3.0.2",
+ "ci-info": "^4.0.0",
+ "ini": "^4.1.2",
+ "nopt": "^7.2.1",
+ "proc-log": "^4.2.0",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.5",
+ "walk-up-path": "^3.0.1"
+ }
+ },
+ "@npmcli/fs": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/git": {
+ "version": "5.0.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/promise-spawn": "^7.0.0",
+ "lru-cache": "^10.0.1",
+ "npm-pick-manifest": "^9.0.0",
+ "proc-log": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^4.0.0"
+ }
+ },
+ "@npmcli/installed-package-contents": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-bundled": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "@npmcli/map-workspaces": {
+ "version": "3.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/name-from-folder": "^2.0.0",
+ "glob": "^10.2.2",
+ "minimatch": "^9.0.0",
+ "read-package-json-fast": "^3.0.0"
+ }
+ },
+ "@npmcli/metavuln-calculator": {
+ "version": "7.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cacache": "^18.0.0",
+ "json-parse-even-better-errors": "^3.0.0",
+ "pacote": "^18.0.0",
+ "proc-log": "^4.1.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/name-from-folder": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/node-gyp": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/package-json": {
+ "version": "5.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^5.0.0",
+ "glob": "^10.2.2",
+ "hosted-git-info": "^7.0.0",
+ "json-parse-even-better-errors": "^3.0.0",
+ "normalize-package-data": "^6.0.0",
+ "proc-log": "^4.0.0",
+ "semver": "^7.5.3"
+ }
+ },
+ "@npmcli/promise-spawn": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "which": "^4.0.0"
+ }
+ },
+ "@npmcli/query": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "@npmcli/redact": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/run-script": {
+ "version": "8.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/node-gyp": "^3.0.0",
+ "@npmcli/package-json": "^5.0.0",
+ "@npmcli/promise-spawn": "^7.0.0",
+ "node-gyp": "^10.0.0",
+ "proc-log": "^4.0.0",
+ "which": "^4.0.0"
+ }
+ },
+ "@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "@sigstore/bundle": {
+ "version": "2.3.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.3.2"
+ }
+ },
+ "@sigstore/core": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/protobuf-specs": {
+ "version": "0.3.2",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/sign": {
+ "version": "2.3.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.0.0",
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "make-fetch-happen": "^13.0.1",
+ "proc-log": "^4.2.0",
+ "promise-retry": "^2.0.1"
+ }
+ },
+ "@sigstore/tuf": {
+ "version": "2.3.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "tuf-js": "^2.2.1"
+ }
+ },
+ "@sigstore/verify": {
+ "version": "1.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.1.0",
+ "@sigstore/protobuf-specs": "^0.3.2"
+ }
+ },
+ "@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@tufjs/models": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^9.0.4"
+ }
+ },
+ "abbrev": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "agent-base": {
+ "version": "7.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4"
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.1",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "archy": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "bin-links": {
+ "version": "4.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cmd-shim": "^6.0.0",
+ "npm-normalize-package-bin": "^3.0.0",
+ "read-cmd-shim": "^4.0.0",
+ "write-file-atomic": "^5.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.3.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "cacache": {
+ "version": "18.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^3.1.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^10.2.2",
+ "lru-cache": "^10.0.1",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^4.0.0",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^3.0.0"
+ }
+ },
+ "chalk": {
+ "version": "5.3.0",
+ "bundled": true,
+ "dev": true
+ },
+ "chownr": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ci-info": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cidr-regex": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ip-regex": "^5.0.0"
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cli-columns": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "cmd-shim": {
+ "version": "6.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "common-ancestor-path": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "dependencies": {
+ "which": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "debug": {
+ "version": "4.3.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "diff": {
+ "version": "5.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "eastasianwidth": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.13",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "bundled": true,
+ "dev": true
+ },
+ "err-code": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "exponential-backoff": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "fastest-levenshtein": {
+ "version": "1.0.16",
+ "bundled": true,
+ "dev": true
+ },
+ "foreground-child": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "fs-minipass": {
+ "version": "3.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "glob": {
+ "version": "10.4.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "path-scurry": "^1.11.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "bundled": true,
+ "dev": true
+ },
+ "hasown": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
+ "hosted-git-info": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^10.0.1"
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "7.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.0.2",
+ "debug": "4"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "ignore-walk": {
+ "version": "6.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimatch": "^9.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ini": {
+ "version": "4.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "init-package-json": {
+ "version": "6.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/package-json": "^5.0.0",
+ "npm-package-arg": "^11.0.0",
+ "promzard": "^1.0.0",
+ "read": "^3.0.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4",
+ "validate-npm-package-name": "^5.0.0"
+ }
+ },
+ "ip-address": {
+ "version": "9.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "jsbn": "1.1.0",
+ "sprintf-js": "^1.1.3"
+ }
+ },
+ "ip-regex": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "is-cidr": {
+ "version": "5.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cidr-regex": "^4.1.1"
+ }
+ },
+ "is-core-module": {
+ "version": "2.13.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "is-lambda": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "jackspeak": {
+ "version": "3.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@isaacs/cliui": "^8.0.2",
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "jsbn": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "json-stringify-nice": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff": {
+ "version": "6.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff-apply": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "libnpmaccess": {
+ "version": "8.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-package-arg": "^11.0.2",
+ "npm-registry-fetch": "^17.0.1"
+ }
+ },
+ "libnpmdiff": {
+ "version": "6.1.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/installed-package-contents": "^2.1.0",
+ "binary-extensions": "^2.3.0",
+ "diff": "^5.1.0",
+ "minimatch": "^9.0.4",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6",
+ "tar": "^6.2.1"
+ }
+ },
+ "libnpmexec": {
+ "version": "8.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/run-script": "^8.1.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6",
+ "proc-log": "^4.2.0",
+ "read": "^3.0.1",
+ "read-package-json-fast": "^3.0.2",
+ "semver": "^7.3.7",
+ "walk-up-path": "^3.0.1"
+ }
+ },
+ "libnpmfund": {
+ "version": "5.0.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^7.5.3"
+ }
+ },
+ "libnpmhook": {
+ "version": "10.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ }
+ },
+ "libnpmorg": {
+ "version": "6.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ }
+ },
+ "libnpmpack": {
+ "version": "7.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^7.5.3",
+ "@npmcli/run-script": "^8.1.0",
+ "npm-package-arg": "^11.0.2",
+ "pacote": "^18.0.6"
+ }
+ },
+ "libnpmpublish": {
+ "version": "9.0.9",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ci-info": "^4.0.0",
+ "normalize-package-data": "^6.0.1",
+ "npm-package-arg": "^11.0.2",
+ "npm-registry-fetch": "^17.0.1",
+ "proc-log": "^4.2.0",
+ "semver": "^7.3.7",
+ "sigstore": "^2.2.0",
+ "ssri": "^10.0.6"
+ }
+ },
+ "libnpmsearch": {
+ "version": "7.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^17.0.1"
+ }
+ },
+ "libnpmteam": {
+ "version": "6.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^17.0.1"
+ }
+ },
+ "libnpmversion": {
+ "version": "6.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^5.0.7",
+ "@npmcli/run-script": "^8.1.0",
+ "json-parse-even-better-errors": "^3.0.2",
+ "proc-log": "^4.2.0",
+ "semver": "^7.3.7"
+ }
+ },
+ "lru-cache": {
+ "version": "10.2.2",
+ "bundled": true,
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "13.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/agent": "^2.0.0",
+ "cacache": "^18.0.0",
+ "http-cache-semantics": "^4.1.1",
+ "is-lambda": "^1.0.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^3.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "proc-log": "^4.2.0",
+ "promise-retry": "^2.0.1",
+ "ssri": "^10.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "9.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "minipass": {
+ "version": "7.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass-collect": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "minipass-fetch": {
+ "version": "3.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.13",
+ "minipass": "^7.0.3",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "minipass-json-stream": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "minipass-sized": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "bundled": true,
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "bundled": true,
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "10.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "glob": "^10.3.10",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^13.0.0",
+ "nopt": "^7.0.0",
+ "proc-log": "^3.0.0",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^4.0.0"
+ },
+ "dependencies": {
+ "proc-log": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "nopt": {
+ "version": "7.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "abbrev": "^2.0.0"
+ }
+ },
+ "normalize-package-data": {
+ "version": "6.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^7.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ }
+ },
+ "npm-audit-report": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "npm-install-checks": {
+ "version": "6.3.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "11.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^7.0.0",
+ "proc-log": "^4.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^5.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "8.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^6.0.4"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "9.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^6.0.0",
+ "npm-normalize-package-bin": "^3.0.0",
+ "npm-package-arg": "^11.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "npm-profile": {
+ "version": "10.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^17.0.1",
+ "proc-log": "^4.0.0"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "17.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/redact": "^2.0.0",
+ "make-fetch-happen": "^13.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^3.0.0",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^11.0.0",
+ "proc-log": "^4.0.0"
+ }
+ },
+ "npm-user-validate": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "pacote": {
+ "version": "18.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^5.0.0",
+ "@npmcli/installed-package-contents": "^2.0.1",
+ "@npmcli/package-json": "^5.1.0",
+ "@npmcli/promise-spawn": "^7.0.0",
+ "@npmcli/run-script": "^8.0.0",
+ "cacache": "^18.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^11.0.0",
+ "npm-packlist": "^8.0.0",
+ "npm-pick-manifest": "^9.0.0",
+ "npm-registry-fetch": "^17.0.0",
+ "proc-log": "^4.0.0",
+ "promise-retry": "^2.0.1",
+ "sigstore": "^2.2.0",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11"
+ }
+ },
+ "parse-conflict-json": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ }
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "path-scurry": {
+ "version": "1.11.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "proc-log": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "proggy": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-all-reject-late": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-call-limit": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-retry": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ }
+ },
+ "promzard": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "read": "^3.0.1"
+ }
+ },
+ "qrcode-terminal": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "read": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mute-stream": "^1.0.0"
+ }
+ },
+ "read-cmd-shim": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "read-package-json-fast": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "retry": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "7.6.2",
+ "bundled": true,
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "sigstore": {
+ "version": "2.3.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^2.3.2",
+ "@sigstore/core": "^1.0.0",
+ "@sigstore/protobuf-specs": "^0.3.2",
+ "@sigstore/sign": "^2.3.2",
+ "@sigstore/tuf": "^2.3.4",
+ "@sigstore/verify": "^1.2.1"
+ }
+ },
+ "smart-buffer": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "socks": {
+ "version": "2.8.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ip-address": "^9.0.5",
+ "smart-buffer": "^4.2.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "8.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.1",
+ "debug": "^4.3.4",
+ "socks": "^2.7.1"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ },
+ "dependencies": {
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ }
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.18",
+ "bundled": true,
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "ssri": {
+ "version": "10.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "string-width-cjs": {
+ "version": "npm:string-width@4.2.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-ansi-cjs": {
+ "version": "npm:strip-ansi@6.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "supports-color": {
+ "version": "9.4.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tar": {
+ "version": "6.2.1",
+ "bundled": true,
+ "dev": 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": {
+ "fs-minipass": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "minipass": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tiny-relative-date": {
+ "version": "1.3.0",
+ "bundled": true,
+ "dev": true
+ },
+ "treeverse": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tuf-js": {
+ "version": "2.2.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/models": "2.0.1",
+ "debug": "^4.3.4",
+ "make-fetch-happen": "^13.0.1"
+ }
+ },
+ "unique-filename": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "unique-slug": "^4.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ },
+ "dependencies": {
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ }
+ }
+ },
+ "validate-npm-package-name": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "walk-up-path": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "which": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "isexe": "^3.1.1"
+ },
+ "dependencies": {
+ "isexe": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "wrap-ansi": {
+ "version": "8.1.0",
+ "bundled": true,
+ "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",
+ "bundled": true,
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "9.2.2",
+ "bundled": true,
+ "dev": true
+ },
+ "string-width": {
+ "version": "5.1.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.0.1"
+ }
+ }
+ }
+ },
+ "wrap-ansi-cjs": {
+ "version": "npm:wrap-ansi@7.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ }
+ }
+ },
+ "write-file-atomic": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "npmlog": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-7.0.1.tgz",
+ "integrity": "sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg==",
+ "requires": {
+ "are-we-there-yet": "^4.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^5.0.0",
+ "set-blocking": "^2.0.0"
+ },
+ "dependencies": {
+ "are-we-there-yet": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz",
+ "integrity": "sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg=="
+ }
+ }
+ },
+ "nyc": {
+ "version": "17.1.0",
+ "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz",
+ "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==",
+ "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": "^3.3.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": "^6.0.2",
+ "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": {
+ "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": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "dependencies": {
+ "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
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.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"
+ }
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "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=="
+ },
+ "object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
+ },
+ "object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="
+ },
+ "object-path": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz",
+ "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA=="
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "one-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+ "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+ "requires": {
+ "fn.name": "1.x.x"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optimism": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz",
+ "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==",
+ "dev": true,
+ "requires": {
+ "@wry/caches": "^1.0.0",
+ "@wry/context": "^0.7.0",
+ "@wry/trie": "^0.4.3",
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "@wry/trie": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz",
+ "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ }
+ },
+ "ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "requires": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "otpauth": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.5.0.tgz",
+ "integrity": "sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==",
+ "requires": {
+ "@noble/hashes": "2.0.1"
+ }
+ },
+ "p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "dev": true
+ },
+ "p-each-series": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz",
+ "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==",
+ "dev": true
+ },
+ "p-filter": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz",
+ "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==",
+ "dev": true,
+ "requires": {
+ "p-map": "^7.0.1"
+ },
+ "dependencies": {
+ "p-map": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz",
+ "integrity": "sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==",
+ "dev": true
+ }
+ }
+ },
+ "p-is-promise": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz",
+ "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "devOptional": 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-reduce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz",
+ "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==",
+ "dev": true
+ },
+ "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==",
+ "dev": 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"
+ }
+ },
+ "package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "devOptional": true
+ },
+ "param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "requires": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "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"
+ }
+ },
+ "parse": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.6.0.tgz",
+ "integrity": "sha512-AZjc8yGo8/iTZFpCXWw/r1qNusiUGWtq9i92/u0jNd+Iupg3EJUSV/OOyTrCeav8NDyo92wVS5O3iKAYPlhlsA==",
+ "requires": {
+ "@babel/runtime": "7.29.2",
+ "@babel/runtime-corejs3": "7.29.2",
+ "crypto-js": "4.2.0",
+ "idb-keyval": "6.2.2",
+ "react-native-crypto-js": "1.0.0",
+ "ws": "8.20.0"
+ }
+ },
+ "parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "parse-ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
+ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "dev": true
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ }
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "requires": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "path-expression-matcher": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz",
+ "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==",
+ "optional": 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==",
+ "dev": 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==",
+ "devOptional": 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.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "devOptional": true,
+ "requires": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "devOptional": true
+ }
+ }
+ },
+ "path-to-regexp": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+ "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true
+ },
+ "pg": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
+ "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
+ "requires": {
+ "pg-cloudflare": "^1.3.0",
+ "pg-connection-string": "^2.11.0",
+ "pg-pool": "^3.11.0",
+ "pg-protocol": "^1.11.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ }
+ },
+ "pg-cloudflare": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
+ "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
+ "optional": true
+ },
+ "pg-connection-string": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz",
+ "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ=="
+ },
+ "pg-cursor": {
+ "version": "2.17.0",
+ "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz",
+ "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==",
+ "peer": true,
+ "requires": {}
+ },
+ "pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
+ },
+ "pg-minify": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.8.0.tgz",
+ "integrity": "sha512-jO/oJOununpx8DzKgvSsWm61P8JjwXlaxSlbbfTBo1nvSWoo/+I6qZYaSN96jm/KDwa5d+JMQwPGgcP6HXDRow=="
+ },
+ "pg-monitor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-3.1.0.tgz",
+ "integrity": "sha512-giK0h52AOO/v8iu6hZCdZ/X9W8oAM9Dm1VReQQtki532X8g4z1LVIm4Z/3cGvDcETWW+Ty0FrtU8iTrGFYIZfA==",
+ "requires": {
+ "picocolors": "1.1.1"
+ }
+ },
+ "pg-pool": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz",
+ "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==",
+ "requires": {}
+ },
+ "pg-promise": {
+ "version": "12.6.0",
+ "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-12.6.0.tgz",
+ "integrity": "sha512-ZnfNn7c0U2p1OWYqoENcke8eSTe+yCGOpuMurExTuot/pe3POodbakE9Sj5MWUsyPpyreARUUPwe4j/5Dfs9Dw==",
+ "requires": {
+ "assert-options": "0.8.3",
+ "pg": "8.18.0",
+ "pg-minify": "1.8.0",
+ "spex": "4.1.0"
+ }
+ },
+ "pg-protocol": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz",
+ "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="
+ },
+ "pg-query-stream": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.12.0.tgz",
+ "integrity": "sha512-H97oiVPQ0+eRqIFOeYMUnjDcv9od7vHHMjiVDAhg2SEzAUr3M/dT83UEV1B+fm+tcVnymI8j2LSp57/+yjF6Fg==",
+ "peer": true,
+ "requires": {
+ "pg-cursor": "^2.17.0"
+ }
+ },
+ "pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "requires": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ }
+ },
+ "pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "requires": {
+ "split2": "^4.1.0"
+ }
+ },
+ "picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+ },
+ "picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-conf": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz",
+ "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "load-json-file": "^4.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true
+ }
+ }
+ },
+ "pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="
+ },
+ "possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="
+ },
+ "postcss": {
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "postcss-values-parser": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz",
+ "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.1.4",
+ "is-url-superb": "^4.0.0",
+ "quote-unquote": "^1.0.0"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true
+ }
+ }
+ },
+ "postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
+ },
+ "postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="
+ },
+ "postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
+ },
+ "postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "requires": {
+ "xtend": "^4.0.0"
+ }
+ },
+ "precinct": {
+ "version": "12.1.2",
+ "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz",
+ "integrity": "sha512-x2qVN3oSOp3D05ihCd8XdkIPuEQsyte7PSxzLqiRgktu79S5Dr1I75/S+zAup8/0cwjoiJTQztE9h0/sWp9bJQ==",
+ "dev": true,
+ "requires": {
+ "@dependents/detective-less": "^5.0.0",
+ "commander": "^12.1.0",
+ "detective-amd": "^6.0.0",
+ "detective-cjs": "^6.0.0",
+ "detective-es6": "^5.0.0",
+ "detective-postcss": "^7.0.0",
+ "detective-sass": "^6.0.0",
+ "detective-scss": "^5.0.0",
+ "detective-stylus": "^5.0.0",
+ "detective-typescript": "^13.0.0",
+ "detective-vue2": "^2.0.3",
+ "module-definition": "^6.0.0",
+ "node-source-walk": "^7.0.0",
+ "postcss": "^8.4.40",
+ "typescript": "^5.5.4"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "precond": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
+ "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="
+ },
+ "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
+ },
+ "prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true
+ },
+ "pretty-ms": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
+ "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
+ "dev": true,
+ "requires": {
+ "parse-ms": "^2.1.0"
+ }
+ },
+ "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==",
+ "dev": 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"
+ }
+ },
+ "process-warning": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz",
+ "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA=="
+ },
+ "promise-limit": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
+ "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="
+ },
+ "promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "requires": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "dependencies": {
+ "retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="
+ }
+ }
+ },
+ "prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true
+ },
+ "proto3-json-serializer": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
+ "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
+ "optional": true,
+ "requires": {
+ "protobufjs": "^7.2.5"
+ }
+ },
+ "protobufjs": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
+ "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
+ "optional": true,
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "dependencies": {
+ "long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "optional": true
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ },
+ "punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+ },
+ "punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "requires": {
+ "side-channel": "^1.1.0"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "quote-unquote": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz",
+ "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==",
+ "dev": true
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "rate-limit-redis": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-4.3.1.tgz",
+ "integrity": "sha512-+a1zU8+D7L8siDK9jb14refQXz60vq427VuiplgnaLk9B2LnvGe/APLTfhwb4uNIL7eWVknh8GnRp/unCj+lMA==",
+ "requires": {}
+ },
+ "raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "requires": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "requires": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ }
+ },
+ "statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="
+ }
+ }
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true
+ }
+ }
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true
+ },
+ "react-native-crypto-js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+ "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+ },
+ "read-package-up": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
+ "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
+ "dev": true,
+ "requires": {
+ "find-up-simple": "^1.0.0",
+ "read-pkg": "^9.0.0",
+ "type-fest": "^4.6.0"
+ }
+ },
+ "read-pkg": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
+ "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
+ "dev": true,
+ "requires": {
+ "@types/normalize-package-data": "^2.4.3",
+ "normalize-package-data": "^6.0.0",
+ "parse-json": "^8.0.0",
+ "type-fest": "^4.6.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz",
+ "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.22.13",
+ "index-to-position": "^0.1.2",
+ "type-fest": "^4.7.1"
+ }
+ }
+ }
+ },
+ "read-pkg-up": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz",
+ "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==",
+ "dev": true,
+ "requires": {
+ "find-up-simple": "^1.0.0",
+ "read-pkg": "^9.0.0",
+ "type-fest": "^4.6.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": 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"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true
+ }
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "redeyed": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz",
+ "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==",
+ "dev": true,
+ "requires": {
+ "esprima": "~4.0.0"
+ }
+ },
+ "redis": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-5.11.0.tgz",
+ "integrity": "sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==",
+ "requires": {
+ "@redis/bloom": "5.11.0",
+ "@redis/client": "5.11.0",
+ "@redis/json": "5.11.0",
+ "@redis/search": "5.11.0",
+ "@redis/time-series": "5.11.0"
+ }
+ },
+ "regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "regenerate-unicode-properties": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2"
+ }
+ },
+ "regexpu-core": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
+ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.2",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.13.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.2.1"
+ }
+ },
+ "registry-auth-token": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz",
+ "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+ "dev": true,
+ "requires": {
+ "@pnpm/npm-conf": "^2.1.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz",
+ "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~3.1.0"
+ }
+ },
+ "rehackt": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
+ "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
+ "dev": true,
+ "requires": {}
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "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-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==",
+ "dev": true
+ },
+ "requirejs": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz",
+ "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==",
+ "dev": true
+ },
+ "requirejs-config-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz",
+ "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.0",
+ "stringify-object": "^3.2.1"
+ }
+ },
+ "requizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
+ "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.21"
+ }
+ },
+ "resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.16.1",
+ "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==",
+ "dev": true
+ },
+ "resolve-dependency-path": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-4.0.0.tgz",
+ "integrity": "sha512-hlY1SybBGm5aYN3PC4rp15MzsJLM1w+MEA/4KU3UBPfz4S0lL3FL6mgv7JgaA8a+ZTeEQAiF1a1BuN2nkqiIlg==",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "lowercase-keys": "^3.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
+ },
+ "retry-request": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+ "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
+ "optional": true,
+ "requires": {
+ "@types/request": "^2.48.8",
+ "extend": "^3.0.2",
+ "teeny-request": "^9.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
+ },
+ "rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "requires": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "dependencies": {
+ "is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
+ }
+ }
+ },
+ "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.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safe-stable-stringify": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz",
+ "integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "sass-lookup": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-6.0.1.tgz",
+ "integrity": "sha512-nl9Wxbj9RjEJA5SSV0hSDoU2zYGtE+ANaDS4OFUR7nYrquvBFvPKZZtQHe3lvnxCcylEDV00KUijjdMTUElcVQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^12.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "seek-bzip": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz",
+ "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.8.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ }
+ }
+ },
+ "semantic-release": {
+ "version": "25.0.3",
+ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-25.0.3.tgz",
+ "integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==",
+ "dev": true,
+ "requires": {
+ "@semantic-release/commit-analyzer": "^13.0.1",
+ "@semantic-release/error": "^4.0.0",
+ "@semantic-release/github": "^12.0.0",
+ "@semantic-release/npm": "^13.1.1",
+ "@semantic-release/release-notes-generator": "^14.1.0",
+ "aggregate-error": "^5.0.0",
+ "cosmiconfig": "^9.0.0",
+ "debug": "^4.0.0",
+ "env-ci": "^11.0.0",
+ "execa": "^9.0.0",
+ "figures": "^6.0.0",
+ "find-versions": "^6.0.0",
+ "get-stream": "^6.0.0",
+ "git-log-parser": "^1.2.0",
+ "hook-std": "^4.0.0",
+ "hosted-git-info": "^9.0.0",
+ "import-from-esm": "^2.0.0",
+ "lodash-es": "^4.17.21",
+ "marked": "^15.0.0",
+ "marked-terminal": "^7.3.0",
+ "micromatch": "^4.0.2",
+ "p-each-series": "^3.0.0",
+ "p-reduce": "^3.0.0",
+ "read-package-up": "^12.0.0",
+ "resolve-from": "^5.0.0",
+ "semver": "^7.3.2",
+ "signale": "^1.2.1",
+ "yargs": "^18.0.0"
+ },
+ "dependencies": {
+ "@semantic-release/error": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz",
+ "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
+ "dev": true
+ },
+ "@semantic-release/npm": {
+ "version": "13.1.5",
+ "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-13.1.5.tgz",
+ "integrity": "sha512-Hq5UxzoatN3LHiq2rTsWS54nCdqJHlsssGERCo8WlvdfFA9LoN0vO+OuKVSjtNapIc/S8C2LBj206wKLHg62mg==",
+ "dev": true,
+ "requires": {
+ "@actions/core": "^3.0.0",
+ "@semantic-release/error": "^4.0.0",
+ "aggregate-error": "^5.0.0",
+ "env-ci": "^11.2.0",
+ "execa": "^9.0.0",
+ "fs-extra": "^11.0.0",
+ "lodash-es": "^4.17.21",
+ "nerf-dart": "^1.0.0",
+ "normalize-url": "^9.0.0",
+ "npm": "^11.6.2",
+ "rc": "^1.2.8",
+ "read-pkg": "^10.0.0",
+ "registry-auth-token": "^5.0.0",
+ "semver": "^7.1.2",
+ "tempy": "^3.0.0"
+ }
+ },
+ "@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true
+ },
+ "aggregate-error": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
+ "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^5.2.0",
+ "indent-string": "^5.0.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true
+ },
+ "clean-stack": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz",
+ "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "5.0.0"
+ }
+ },
+ "cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "requires": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "dev": true
+ },
+ "execa": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
+ "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
+ "dev": true,
+ "requires": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "cross-spawn": "^7.0.3",
+ "figures": "^6.1.0",
+ "get-stream": "^9.0.0",
+ "human-signals": "^7.0.0",
+ "is-plain-obj": "^4.1.0",
+ "is-stream": "^4.0.1",
+ "npm-run-path": "^5.2.0",
+ "pretty-ms": "^9.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^4.0.0",
+ "yoctocolors": "^2.0.0"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
+ "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
+ "dev": true,
+ "requires": {
+ "@sec-ant/readable-stream": "^0.4.1",
+ "is-stream": "^4.0.1"
+ }
+ }
+ }
+ },
+ "hook-std": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-4.0.0.tgz",
+ "integrity": "sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz",
+ "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^11.1.0"
+ }
+ },
+ "human-signals": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
+ "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
+ "dev": true
+ },
+ "import-from-esm": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
+ "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.3.4",
+ "import-meta-resolve": "^4.0.0"
+ }
+ },
+ "indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true
+ },
+ "index-to-position": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz",
+ "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
+ "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "dev": true
+ },
+ "marked": {
+ "version": "15.0.12",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
+ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz",
+ "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^9.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ }
+ },
+ "normalize-url": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-9.0.0.tgz",
+ "integrity": "sha512-z9nC87iaZXXySbWWtTHfCFJyFvKaUAW6lODhikG7ILSbVgmwuFjUqkgnheHvAUcGedO29e2QGBRXMUD64aurqQ==",
+ "dev": true
+ },
+ "npm": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/npm/-/npm-11.12.0.tgz",
+ "integrity": "sha512-xPhOap4ZbJWyd7DAOukP564WFwNSGu/2FeTRFHhiiKthcauxhH/NpkJAQm24xD+cAn8av5tQ00phi98DqtfLsg==",
+ "dev": true,
+ "requires": {
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/config": "^10.8.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/map-workspaces": "^5.0.3",
+ "@npmcli/metavuln-calculator": "^9.0.3",
+ "@npmcli/package-json": "^7.0.5",
+ "@npmcli/promise-spawn": "^9.0.1",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.4",
+ "@sigstore/tuf": "^4.0.2",
+ "abbrev": "^4.0.0",
+ "archy": "~1.0.0",
+ "cacache": "^20.0.4",
+ "chalk": "^5.6.2",
+ "ci-info": "^4.4.0",
+ "fastest-levenshtein": "^1.0.16",
+ "fs-minipass": "^3.0.3",
+ "glob": "^13.0.6",
+ "graceful-fs": "^4.2.11",
+ "hosted-git-info": "^9.0.2",
+ "ini": "^6.0.0",
+ "init-package-json": "^8.2.5",
+ "is-cidr": "^6.0.3",
+ "json-parse-even-better-errors": "^5.0.0",
+ "libnpmaccess": "^10.0.3",
+ "libnpmdiff": "^8.1.5",
+ "libnpmexec": "^10.2.5",
+ "libnpmfund": "^7.0.19",
+ "libnpmorg": "^8.0.1",
+ "libnpmpack": "^9.1.5",
+ "libnpmpublish": "^11.1.3",
+ "libnpmsearch": "^9.0.1",
+ "libnpmteam": "^8.0.2",
+ "libnpmversion": "^8.0.3",
+ "make-fetch-happen": "^15.0.5",
+ "minimatch": "^10.2.4",
+ "minipass": "^7.1.3",
+ "minipass-pipeline": "^1.2.4",
+ "ms": "^2.1.2",
+ "node-gyp": "^12.2.0",
+ "nopt": "^9.0.0",
+ "npm-audit-report": "^7.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.2",
+ "npm-pick-manifest": "^11.0.3",
+ "npm-profile": "^12.0.1",
+ "npm-registry-fetch": "^19.1.1",
+ "npm-user-validate": "^4.0.0",
+ "p-map": "^7.0.4",
+ "pacote": "^21.5.0",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.1.0",
+ "qrcode-terminal": "^0.12.0",
+ "read": "^5.0.1",
+ "semver": "^7.7.4",
+ "spdx-expression-parse": "^4.0.0",
+ "ssri": "^13.0.1",
+ "supports-color": "^10.2.2",
+ "tar": "^7.5.11",
+ "text-table": "~0.2.0",
+ "tiny-relative-date": "^2.0.2",
+ "treeverse": "^3.0.0",
+ "validate-npm-package-name": "^7.0.2",
+ "which": "^6.0.1"
+ },
+ "dependencies": {
+ "@gar/promise-retry": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.4"
+ }
+ },
+ "@isaacs/string-locale-compare": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/agent": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^11.2.1",
+ "socks-proxy-agent": "^8.0.3"
+ }
+ },
+ "@npmcli/arborist": {
+ "version": "9.4.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@isaacs/string-locale-compare": "^1.1.0",
+ "@npmcli/fs": "^5.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/metavuln-calculator": "^9.0.2",
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/query": "^5.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "bin-links": "^6.0.0",
+ "cacache": "^20.0.1",
+ "common-ancestor-path": "^2.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-stringify-nice": "^1.1.4",
+ "lru-cache": "^11.2.1",
+ "minimatch": "^10.0.3",
+ "nopt": "^9.0.0",
+ "npm-install-checks": "^8.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "pacote": "^21.0.2",
+ "parse-conflict-json": "^5.0.1",
+ "proc-log": "^6.0.0",
+ "proggy": "^4.0.0",
+ "promise-all-reject-late": "^1.0.0",
+ "promise-call-limit": "^3.0.1",
+ "semver": "^7.3.7",
+ "ssri": "^13.0.0",
+ "treeverse": "^3.0.0",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "@npmcli/config": {
+ "version": "10.8.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/map-workspaces": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "ini": "^6.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "@npmcli/fs": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/git": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "ini": "^6.0.0",
+ "lru-cache": "^11.2.1",
+ "npm-pick-manifest": "^11.0.1",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "which": "^6.0.0"
+ }
+ },
+ "@npmcli/installed-package-contents": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-bundled": "^5.0.0",
+ "npm-normalize-package-bin": "^5.0.0"
+ }
+ },
+ "@npmcli/map-workspaces": {
+ "version": "5.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/name-from-folder": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "glob": "^13.0.0",
+ "minimatch": "^10.0.3"
+ }
+ },
+ "@npmcli/metavuln-calculator": {
+ "version": "9.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cacache": "^20.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "pacote": "^21.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/name-from-folder": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/node-gyp": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/package-json": {
+ "version": "7.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^7.0.0",
+ "glob": "^13.0.0",
+ "hosted-git-info": "^9.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.5.3",
+ "spdx-expression-parse": "^4.0.0"
+ }
+ },
+ "@npmcli/promise-spawn": {
+ "version": "9.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "which": "^6.0.0"
+ }
+ },
+ "@npmcli/query": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
+ "@npmcli/redact": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@npmcli/run-script": {
+ "version": "10.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/node-gyp": "^5.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "node-gyp": "^12.1.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "@sigstore/bundle": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.5.0"
+ }
+ },
+ "@sigstore/core": {
+ "version": "3.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/protobuf-specs": {
+ "version": "0.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@sigstore/sign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.2",
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.2.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "make-fetch-happen": "^15.0.4",
+ "proc-log": "^6.1.0"
+ }
+ },
+ "@sigstore/tuf": {
+ "version": "4.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "tuf-js": "^4.1.0"
+ }
+ },
+ "@sigstore/verify": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0"
+ }
+ },
+ "@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "@tufjs/models": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^10.1.1"
+ }
+ },
+ "abbrev": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "agent-base": {
+ "version": "7.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "2.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "archy": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "bundled": true,
+ "dev": true
+ },
+ "bin-links": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cmd-shim": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "read-cmd-shim": "^6.0.0",
+ "write-file-atomic": "^7.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "cacache": {
+ "version": "20.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^5.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^13.0.0",
+ "lru-cache": "^11.1.0",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^7.0.2",
+ "ssri": "^13.0.0"
+ }
+ },
+ "chalk": {
+ "version": "5.6.2",
+ "bundled": true,
+ "dev": true
+ },
+ "chownr": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "ci-info": {
+ "version": "4.4.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cidr-regex": {
+ "version": "5.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "cmd-shim": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "common-ancestor-path": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "debug": {
+ "version": "4.4.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.3"
+ }
+ },
+ "diff": {
+ "version": "8.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "bundled": true,
+ "dev": true
+ },
+ "exponential-backoff": {
+ "version": "3.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "fastest-levenshtein": {
+ "version": "1.0.16",
+ "bundled": true,
+ "dev": true
+ },
+ "fs-minipass": {
+ "version": "3.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "glob": {
+ "version": "13.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "bundled": true,
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "9.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^11.1.0"
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "7.0.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.7.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "ignore-walk": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimatch": "^10.0.3"
+ }
+ },
+ "ini": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "init-package-json": {
+ "version": "8.2.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/package-json": "^7.0.0",
+ "npm-package-arg": "^13.0.0",
+ "promzard": "^3.0.1",
+ "read": "^5.0.1",
+ "semver": "^7.7.2",
+ "validate-npm-package-name": "^7.0.0"
+ }
+ },
+ "ip-address": {
+ "version": "10.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "is-cidr": {
+ "version": "6.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cidr-regex": "^5.0.1"
+ }
+ },
+ "isexe": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "json-stringify-nice": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff": {
+ "version": "6.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "just-diff-apply": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "libnpmaccess": {
+ "version": "10.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmdiff": {
+ "version": "8.1.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "binary-extensions": "^3.0.0",
+ "diff": "^8.0.2",
+ "minimatch": "^10.0.3",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "tar": "^7.5.1"
+ }
+ },
+ "libnpmexec": {
+ "version": "10.2.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2",
+ "proc-log": "^6.0.0",
+ "read": "^5.0.1",
+ "semver": "^7.3.7",
+ "signal-exit": "^4.1.0",
+ "walk-up-path": "^4.0.0"
+ }
+ },
+ "libnpmfund": {
+ "version": "7.0.19",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2"
+ }
+ },
+ "libnpmorg": {
+ "version": "8.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmpack": {
+ "version": "9.1.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/arborist": "^9.4.2",
+ "@npmcli/run-script": "^10.0.0",
+ "npm-package-arg": "^13.0.0",
+ "pacote": "^21.0.2"
+ }
+ },
+ "libnpmpublish": {
+ "version": "11.1.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/package-json": "^7.0.0",
+ "ci-info": "^4.0.0",
+ "npm-package-arg": "^13.0.0",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0"
+ }
+ },
+ "libnpmsearch": {
+ "version": "9.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmteam": {
+ "version": "8.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "aproba": "^2.0.0",
+ "npm-registry-fetch": "^19.0.0"
+ }
+ },
+ "libnpmversion": {
+ "version": "8.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "json-parse-even-better-errors": "^5.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.7"
+ }
+ },
+ "lru-cache": {
+ "version": "11.2.7",
+ "bundled": true,
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "15.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/agent": "^4.0.0",
+ "@npmcli/redact": "^4.0.0",
+ "cacache": "^20.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^1.0.0",
+ "proc-log": "^6.0.0",
+ "ssri": "^13.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "10.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.2"
+ }
+ },
+ "minipass": {
+ "version": "7.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass-collect": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "minipass-fetch": {
+ "version": "5.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "iconv-lite": "^0.7.2",
+ "minipass": "^7.0.3",
+ "minipass-sized": "^2.0.0",
+ "minizlib": "^3.0.1"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ },
+ "dependencies": {
+ "minipass": {
+ "version": "3.3.6",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "minipass-sized": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.1.2"
+ }
+ },
+ "minizlib": {
+ "version": "3.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "bundled": true,
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "negotiator": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "12.2.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^15.0.0",
+ "nopt": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.5.4",
+ "tinyglobby": "^0.2.12",
+ "which": "^6.0.0"
+ }
+ },
+ "nopt": {
+ "version": "9.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "abbrev": "^4.0.0"
+ }
+ },
+ "npm-audit-report": {
+ "version": "7.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^5.0.0"
+ }
+ },
+ "npm-install-checks": {
+ "version": "8.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "13.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^9.0.0",
+ "proc-log": "^6.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^7.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "10.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^8.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "11.0.3",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^8.0.0",
+ "npm-normalize-package-bin": "^5.0.0",
+ "npm-package-arg": "^13.0.0",
+ "semver": "^7.3.5"
+ }
+ },
+ "npm-profile": {
+ "version": "12.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "19.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@npmcli/redact": "^4.0.0",
+ "jsonparse": "^1.3.1",
+ "make-fetch-happen": "^15.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^5.0.0",
+ "minizlib": "^3.0.1",
+ "npm-package-arg": "^13.0.0",
+ "proc-log": "^6.0.0"
+ }
+ },
+ "npm-user-validate": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "p-map": {
+ "version": "7.0.4",
+ "bundled": true,
+ "dev": true
+ },
+ "pacote": {
+ "version": "21.5.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@gar/promise-retry": "^1.0.0",
+ "@npmcli/git": "^7.0.0",
+ "@npmcli/installed-package-contents": "^4.0.0",
+ "@npmcli/package-json": "^7.0.0",
+ "@npmcli/promise-spawn": "^9.0.0",
+ "@npmcli/run-script": "^10.0.0",
+ "cacache": "^20.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^13.0.0",
+ "npm-packlist": "^10.0.1",
+ "npm-pick-manifest": "^11.0.1",
+ "npm-registry-fetch": "^19.0.0",
+ "proc-log": "^6.0.0",
+ "sigstore": "^4.0.0",
+ "ssri": "^13.0.0",
+ "tar": "^7.4.3"
+ }
+ },
+ "parse-conflict-json": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^5.0.0",
+ "just-diff": "^6.0.0",
+ "just-diff-apply": "^5.2.0"
+ }
+ },
+ "path-scurry": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "7.1.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "proc-log": {
+ "version": "6.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "proggy": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-all-reject-late": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "promise-call-limit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "promzard": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "read": "^5.0.0"
+ }
+ },
+ "qrcode-terminal": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true
+ },
+ "read": {
+ "version": "5.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "mute-stream": "^3.0.0"
+ }
+ },
+ "read-cmd-shim": {
+ "version": "6.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "7.7.4",
+ "bundled": true,
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "sigstore": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@sigstore/bundle": "^4.0.0",
+ "@sigstore/core": "^3.1.0",
+ "@sigstore/protobuf-specs": "^0.5.0",
+ "@sigstore/sign": "^4.1.0",
+ "@sigstore/tuf": "^4.0.1",
+ "@sigstore/verify": "^3.1.0"
+ }
+ },
+ "smart-buffer": {
+ "version": "4.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "socks": {
+ "version": "2.8.7",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "8.0.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.5.0",
+ "bundled": true,
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.23",
+ "bundled": true,
+ "dev": true
+ },
+ "ssri": {
+ "version": "13.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minipass": "^7.0.3"
+ }
+ },
+ "supports-color": {
+ "version": "10.2.2",
+ "bundled": true,
+ "dev": true
+ },
+ "tar": {
+ "version": "7.5.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tiny-relative-date": {
+ "version": "2.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "tinyglobby": {
+ "version": "0.2.15",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "dependencies": {
+ "fdir": {
+ "version": "6.5.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {}
+ },
+ "picomatch": {
+ "version": "4.0.3",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "treeverse": {
+ "version": "3.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "tuf-js": {
+ "version": "4.1.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "@tufjs/models": "4.1.0",
+ "debug": "^4.4.3",
+ "make-fetch-happen": "^15.0.1"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "validate-npm-package-name": {
+ "version": "7.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "walk-up-path": {
+ "version": "4.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "which": {
+ "version": "6.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "isexe": "^4.0.0"
+ }
+ },
+ "write-file-atomic": {
+ "version": "7.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "signal-exit": "^4.0.1"
+ }
+ },
+ "yallist": {
+ "version": "5.0.0",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "requires": {
+ "path-key": "^4.0.0"
+ }
+ },
+ "p-reduce": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
+ "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
+ "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.26.2",
+ "index-to-position": "^1.1.0",
+ "type-fest": "^4.39.1"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true
+ }
+ }
+ },
+ "parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true
+ },
+ "pretty-ms": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
+ "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
+ "dev": true,
+ "requires": {
+ "parse-ms": "^4.0.0"
+ }
+ },
+ "read-package-up": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-12.0.0.tgz",
+ "integrity": "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==",
+ "dev": true,
+ "requires": {
+ "find-up-simple": "^1.0.1",
+ "read-pkg": "^10.0.0",
+ "type-fest": "^5.2.0"
+ }
+ },
+ "read-pkg": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.1.0.tgz",
+ "integrity": "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==",
+ "dev": true,
+ "requires": {
+ "@types/normalize-package-data": "^2.4.4",
+ "normalize-package-data": "^8.0.0",
+ "parse-json": "^8.3.0",
+ "type-fest": "^5.4.4",
+ "unicorn-magic": "^0.4.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": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^6.2.2"
+ }
+ },
+ "strip-final-newline": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
+ "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz",
+ "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==",
+ "dev": true,
+ "requires": {
+ "tagged-tag": "^1.0.0"
+ }
+ },
+ "unicorn-magic": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+ "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true
+ }
+ }
+ },
+ "semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="
+ },
+ "semver-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
+ "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.3.5"
+ }
+ },
+ "semver-regex": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz",
+ "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==",
+ "dev": true
+ },
+ "send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "requires": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="
+ },
+ "mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "requires": {
+ "mime-db": "^1.54.0"
+ }
+ }
+ }
+ },
+ "serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "requires": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.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=="
+ },
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "requires": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ }
+ },
+ "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==",
+ "devOptional": 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==",
+ "devOptional": true
+ },
+ "showdown": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
+ "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^9.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true
+ }
+ }
+ },
+ "side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ }
+ },
+ "side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ }
+ },
+ "side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "requires": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ }
+ },
+ "side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "requires": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ }
+ },
+ "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
+ },
+ "signale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz",
+ "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.3.2",
+ "figures": "^2.0.0",
+ "pkg-conf": "^2.1.0"
+ },
+ "dependencies": {
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ }
+ }
+ },
+ "skin-tone": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
+ "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
+ "dev": true,
+ "requires": {
+ "unicode-emoji-modifier-base": "^1.0.0"
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
+ "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^6.2.3",
+ "is-fullwidth-code-point": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "requires": {
+ "get-east-asian-width": "^1.3.1"
+ }
+ }
+ }
+ },
+ "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
+ },
+ "source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sparse-bitfield": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+ "requires": {
+ "memory-pager": "^1.0.2"
+ }
+ },
+ "spawn-error-forwarder": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz",
+ "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==",
+ "dev": true
+ },
+ "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": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "spdx-correct": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "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
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz",
+ "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==",
+ "dev": true
+ },
+ "spex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/spex/-/spex-4.1.0.tgz",
+ "integrity": "sha512-ktgNAQ1X9x1A3IMChM6XBDeVjhGPbLgPQ8aEzGOaUIhZTnLeJSBApvi3gXT789hee6h73N3jOeWkXDwoPbYT/A=="
+ },
+ "split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+ },
+ "stream-combiner2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==",
+ "dev": true,
+ "requires": {
+ "duplexer2": "~0.1.0",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "optional": true,
+ "requires": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "optional": true
+ },
+ "stream-to-array": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz",
+ "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.1.0"
+ }
+ },
+ "streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
+ },
+ "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==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ },
+ "dependencies": {
+ "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=="
+ }
+ }
+ },
+ "string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "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==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^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==",
+ "devOptional": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "dev": true,
+ "requires": {
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ },
+ "dependencies": {
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "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==",
+ "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==",
+ "devOptional": 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-dirs": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz",
+ "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==",
+ "dev": true,
+ "requires": {
+ "is-natural-number": "^4.0.1"
+ }
+ },
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "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
+ },
+ "strnum": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz",
+ "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==",
+ "optional": true
+ },
+ "stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+ "optional": true
+ },
+ "stylus-lookup": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-6.0.0.tgz",
+ "integrity": "sha512-RaWKxAvPnIXrdby+UWCr1WRfa+lrPMSJPySte4Q6a+rWyjeJyFOLJxr5GrAVfcMCsfVlCuzTAJ/ysYT8p8do7Q==",
+ "dev": true,
+ "requires": {
+ "commander": "^12.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true
+ }
+ }
+ },
+ "subscriptions-transport-ws": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz",
+ "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "backo2": "^1.0.2",
+ "eventemitter3": "^3.1.0",
+ "iterall": "^1.2.1",
+ "symbol-observable": "^1.0.4",
+ "ws": "^5.2.0 || ^6.0.0 || ^7.0.0"
+ },
+ "dependencies": {
+ "symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {}
+ }
+ }
+ },
+ "super-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz",
+ "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==",
+ "dev": true,
+ "requires": {
+ "function-timeout": "^1.0.1",
+ "time-span": "^5.1.0"
+ }
+ },
+ "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"
+ }
+ },
+ "supports-hyperlinks": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
+ "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "dependencies": {
+ "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
+ },
+ "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
+ },
+ "symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "dev": true
+ },
+ "tagged-tag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
+ "dev": true
+ },
+ "tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "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==",
+ "dev": 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"
+ }
+ },
+ "tar-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
+ "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
+ "dev": true,
+ "requires": {
+ "bl": "^1.0.0",
+ "buffer-alloc": "^1.2.0",
+ "end-of-stream": "^1.0.0",
+ "fs-constants": "^1.0.0",
+ "readable-stream": "^2.3.0",
+ "to-buffer": "^1.1.1",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "bl": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
+ "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.3.5",
+ "safe-buffer": "^5.1.1"
+ }
+ }
+ }
+ },
+ "teeny-request": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
+ "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
+ "optional": true,
+ "requires": {
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.9",
+ "stream-events": "^1.0.5",
+ "uuid": "^9.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "optional": true,
+ "requires": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "optional": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "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"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "optional": true
+ },
+ "uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "optional": true
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "temp-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz",
+ "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==",
+ "dev": true
+ },
+ "tempy": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz",
+ "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==",
+ "dev": true,
+ "requires": {
+ "is-stream": "^3.0.0",
+ "temp-dir": "^3.0.0",
+ "type-fest": "^2.12.2",
+ "unique-string": "^3.0.0"
+ },
+ "dependencies": {
+ "is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "dev": true
+ }
+ }
+ },
+ "terser": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.0.tgz",
+ "integrity": "sha512-Y/SblUl5kEyEFzhMAQdsxVHh+utAxd4IuRNJzKywY/4uzSogh3G219jqbDDxYu4MXO9CzY3tSEqmZvW6AoEDJw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": 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"
+ }
+ },
+ "text-extensions": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+ "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+ "dev": true
+ },
+ "text-hex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
+ },
+ "thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "time-span": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz",
+ "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==",
+ "dev": true,
+ "requires": {
+ "convert-hrtime": "^5.0.0"
+ }
+ },
+ "tinyexec": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz",
+ "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==",
+ "dev": true
+ },
+ "tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "requires": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "dependencies": {
+ "fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "requires": {}
+ },
+ "picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true
+ }
+ }
+ },
+ "to-buffer": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
+ "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
+ "requires": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ }
+ }
+ },
+ "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"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
+ },
+ "tr46": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "requires": {
+ "punycode": "^2.3.1"
+ }
+ },
+ "traverse": {
+ "version": "0.6.7",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz",
+ "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==",
+ "dev": true
+ },
+ "triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="
+ },
+ "ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "ts-graphviz": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-2.1.4.tgz",
+ "integrity": "sha512-0g465/ES70H0h5rcLUqaenKqNYekQaR9W0m0xUGy3FxueGujpGr+0GN2YWlgLIYSE2Xg0W7Uq1Qqnn7Cg+Af2w==",
+ "dev": true,
+ "requires": {
+ "@ts-graphviz/adapter": "^2.0.5",
+ "@ts-graphviz/ast": "^2.0.5",
+ "@ts-graphviz/common": "^2.1.4",
+ "@ts-graphviz/core": "^2.0.5"
+ }
+ },
+ "ts-invariant": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
+ "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+ "dev": true,
+ "requires": {
+ "json5": "^2.2.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true
+ }
+ }
+ },
+ "tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+ },
+ "tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "dev": true
+ },
+ "tv4": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz",
+ "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw=="
+ },
+ "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-fest": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz",
+ "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "requires": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="
+ },
+ "mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "requires": {
+ "mime-db": "^1.54.0"
+ }
+ }
+ }
+ },
+ "typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "requires": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ }
+ },
+ "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"
+ }
+ },
+ "typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true
+ },
+ "typescript-eslint": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
+ "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/eslint-plugin": "8.58.0",
+ "@typescript-eslint/parser": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0"
+ },
+ "dependencies": {
+ "@typescript-eslint/types": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^4.0.2"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^5.0.5"
+ }
+ },
+ "ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "requires": {}
+ }
+ }
+ },
+ "uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.18.0",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz",
+ "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==",
+ "dev": true,
+ "optional": true
+ },
+ "unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ }
+ },
+ "underscore": {
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
+ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
+ "dev": true
+ },
+ "undici": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
+ "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
+ "dev": true
+ },
+ "undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+ "dev": true
+ },
+ "unicode-emoji-modifier-base": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
+ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
+ "dev": true
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
+ "dev": true
+ },
+ "unicorn-magic": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
+ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+ "dev": true
+ },
+ "unique-string": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
+ "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==",
+ "dev": true,
+ "requires": {
+ "crypto-random-string": "^4.0.0"
+ }
+ },
+ "universal-user-agent": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
+ "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
+ "dev": true
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
+ },
+ "update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ }
+ },
+ "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"
+ }
+ },
+ "url-join": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz",
+ "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ },
+ "vasync": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
+ "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
+ "requires": {
+ "verror": "1.10.0"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ }
+ }
+ },
+ "verror": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
+ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
+ }
+ }
+ },
+ "walkdir": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+ "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+ "dev": true
+ },
+ "wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "requires": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "web-push": {
+ "version": "3.6.7",
+ "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz",
+ "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==",
+ "requires": {
+ "asn1.js": "^5.3.0",
+ "http_ece": "1.2.0",
+ "https-proxy-agent": "^7.0.0",
+ "jws": "^4.0.0",
+ "minimist": "^1.2.5"
+ }
+ },
+ "web-streams-polyfill": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
+ "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
+ },
+ "webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
+ },
+ "whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="
+ },
+ "whatwg-url": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "requires": {
+ "tr46": "^5.1.0",
+ "webidl-conversions": "^7.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "devOptional": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "requires": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ }
+ },
+ "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==",
+ "requires": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "winston": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
+ "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
+ "requires": {
+ "@colors/colors": "^1.6.0",
+ "@dabh/diagnostics": "^2.0.8",
+ "async": "^3.2.3",
+ "is-stream": "^2.0.0",
+ "logform": "^2.7.0",
+ "one-time": "^1.0.0",
+ "readable-stream": "^3.4.0",
+ "safe-stable-stringify": "^2.3.1",
+ "stack-trace": "0.0.x",
+ "triple-beam": "^1.3.0",
+ "winston-transport": "^4.9.0"
+ },
+ "dependencies": {
+ "@colors/colors": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "winston-daily-rotate-file": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz",
+ "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==",
+ "requires": {
+ "file-stream-rotator": "^0.6.1",
+ "object-hash": "^3.0.0",
+ "triple-beam": "^1.4.1",
+ "winston-transport": "^4.7.0"
+ }
+ },
+ "winston-transport": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+ "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+ "requires": {
+ "logform": "^2.7.0",
+ "readable-stream": "^3.6.2",
+ "triple-beam": "^1.3.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true
+ },
+ "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"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "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==",
+ "dev": 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==",
+ "dev": true
+ }
+ }
+ },
+ "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==",
+ "devOptional": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "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"
+ }
+ },
+ "ws": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
+ "requires": {}
+ },
+ "xmlcreate": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
+ "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "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
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
+ "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"
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "devOptional": true
+ },
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "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==",
+ "devOptional": true
+ },
+ "yoctocolors": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
+ "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==",
+ "dev": true
+ },
+ "zen-observable": {
+ "version": "0.8.15",
+ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
+ "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
+ "dev": true
+ },
+ "zen-observable-ts": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
+ "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
+ "dev": true,
+ "requires": {
+ "zen-observable": "0.8.15"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 8d121f6e43..7edb5697d1 100644
--- a/package.json
+++ b/package.json
@@ -1,85 +1,172 @@
{
"name": "parse-server",
- "version": "2.2.17",
+ "version": "9.9.0",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
"type": "git",
- "url": "https://github.com/ParsePlatform/parse-server"
+ "url": "https://github.com/parse-community/parse-server"
},
"files": [
"bin/",
"lib/",
- "public_html/",
+ "public/",
"views/",
"LICENSE",
- "PATENTS",
- "README.md"
+ "NOTICE",
+ "postinstall.js",
+ "README.md",
+ "types"
],
- "license": "BSD-3-Clause",
+ "license": "Apache-2.0",
"dependencies": {
- "babel-polyfill": "6.8.0",
- "babel-runtime": "6.6.1",
- "bcrypt-nodejs": "0.0.3",
- "body-parser": "1.15.2",
- "colors": "1.1.2",
- "commander": "2.9.0",
- "deepcopy": "0.6.3",
- "express": "4.14.0",
+ "@apollo/server": "5.5.0",
+ "@as-integrations/express5": "1.1.2",
+ "@fastify/busboy": "3.2.0",
+ "@graphql-tools/merge": "9.1.7",
+ "@graphql-tools/schema": "10.0.31",
+ "@graphql-tools/utils": "11.0.0",
+ "@parse/fs-files-adapter": "3.0.0",
+ "@parse/push-adapter": "8.4.0",
+ "bcryptjs": "3.0.3",
+ "commander": "14.0.3",
+ "cors": "2.8.6",
+ "express": "5.2.1",
+ "express-rate-limit": "8.3.1",
+ "follow-redirects": "1.15.11",
+ "graphql": "16.13.2",
+ "graphql-list-fields": "2.0.4",
+ "graphql-relay": "0.10.2",
+ "graphql-upload": "15.0.2",
"intersect": "1.0.1",
- "lodash": "4.14.0",
- "lru-cache": "4.0.1",
- "mailgun-js": "0.7.10",
- "mime": "1.3.4",
- "mongodb": "2.2.4",
- "multer": "1.1.0",
- "parse": "1.9.0",
- "parse-server-fs-adapter": "1.0.0",
- "parse-server-push-adapter": "1.0.4",
- "parse-server-s3-adapter": "1.0.4",
- "parse-server-simple-mailgun-adapter": "1.0.0",
- "pg-promise": "5.2.5",
- "redis": "2.6.2",
- "request": "2.74.0",
- "request-promise": "4.0.1",
- "semver": "^5.2.0",
- "tv4": "1.2.7",
- "winston": "2.2.0",
- "winston-daily-rotate-file": "1.2.0",
- "ws": "1.1.1"
+ "jsonwebtoken": "9.0.3",
+ "jwks-rsa": "3.2.0",
+ "ldapjs": "3.0.7",
+ "lodash": "4.18.1",
+ "lru-cache": "11.2.7",
+ "mime": "4.1.0",
+ "mongodb": "7.1.0",
+ "mustache": "4.2.0",
+ "otpauth": "9.5.0",
+ "parse": "8.6.0",
+ "path-to-regexp": "8.4.2",
+ "pg-monitor": "3.1.0",
+ "pg-promise": "12.6.0",
+ "pluralize": "8.0.0",
+ "punycode": "2.3.1",
+ "rate-limit-redis": "4.3.1",
+ "redis": "5.11.0",
+ "semver": "7.7.4",
+ "tv4": "1.3.0",
+ "winston": "3.19.0",
+ "winston-daily-rotate-file": "5.0.0",
+ "ws": "8.20.0"
},
"devDependencies": {
- "babel-cli": "6.11.4",
- "babel-core": "6.11.4",
- "babel-plugin-syntax-flow": "6.8.0",
- "babel-plugin-transform-flow-strip-types": "6.8.0",
- "babel-preset-es2015": "6.6.0",
- "babel-preset-stage-0": "6.5.0",
- "babel-register": "6.9.0",
- "codecov": "1.0.1",
- "cross-env": "2.0.0",
- "deep-diff": "0.3.4",
- "gaze": "1.1.0",
- "istanbul": "1.0.0-alpha.1",
- "jasmine": "2.4.1",
- "mongodb-runner": "3.3.2",
- "nodemon": "1.10.0"
+ "@actions/core": "3.0.0",
+ "@apollo/client": "3.13.8",
+ "@babel/cli": "7.28.6",
+ "@babel/core": "7.29.0",
+ "@babel/eslint-parser": "7.28.6",
+ "@babel/plugin-proposal-object-rest-spread": "7.20.7",
+ "@babel/plugin-transform-flow-strip-types": "7.27.1",
+ "@babel/preset-env": "7.29.2",
+ "@babel/preset-typescript": "7.27.1",
+ "@saithodev/semantic-release-backmerge": "4.0.1",
+ "@semantic-release/changelog": "6.0.3",
+ "@semantic-release/commit-analyzer": "13.0.1",
+ "@semantic-release/git": "10.0.1",
+ "@semantic-release/github": "12.0.6",
+ "@semantic-release/npm": "13.0.0",
+ "@semantic-release/release-notes-generator": "14.1.0",
+ "all-node-versions": "13.0.1",
+ "apollo-upload-client": "18.0.1",
+ "clean-jsdoc-theme": "4.3.0",
+ "cross-env": "7.0.3",
+ "deep-diff": "1.0.2",
+ "eslint": "9.27.0",
+ "eslint-plugin-expect-type": "0.6.2",
+ "eslint-plugin-unused-imports": "4.4.1",
+ "form-data": "4.0.5",
+ "globals": "17.3.0",
+ "graphql-tag": "2.12.6",
+ "jasmine": "6.1.0",
+ "jasmine-spec-reporter": "7.0.0",
+ "jsdoc": "4.0.5",
+ "jsdoc-babel": "0.5.0",
+ "lint-staged": "16.4.0",
+ "m": "1.10.0",
+ "madge": "8.0.0",
+ "mock-files-adapter": "file:spec/dependencies/mock-files-adapter",
+ "mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
+ "mongodb-runner": "5.9.3",
+ "node-abort-controller": "3.1.1",
+ "node-fetch": "3.3.2",
+ "nyc": "17.1.0",
+ "prettier": "3.8.1",
+ "semantic-release": "25.0.3",
+ "typescript": "5.9.3",
+ "typescript-eslint": "8.58.0",
+ "yaml": "2.8.3"
},
"scripts": {
- "dev": "npm run build && node bin/dev",
- "build": "babel src/ -d lib/",
- "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 mongodb-runner start",
- "test": "cross-env NODE_ENV=test TESTING=1 babel-node $COVERAGE_OPTION ./node_modules/.bin/jasmine",
- "test:win": "npm run pretest && cross-env NODE_ENV=test TESTING=1 babel-node ./node_modules/.bin/istanbul cover jasmine && npm run posttest",
- "posttest": "mongodb-runner stop",
- "coverage": "cross-env COVERAGE_OPTION='./node_modules/.bin/istanbul cover' npm test",
+ "ci:check": "node ./ci/ciCheck.js",
+ "ci:checkNodeEngine": "node ./ci/nodeEngineCheck.js",
+ "ci:definitionsCheck": "node ./ci/definitionsCheck.js",
+ "definitions": "node ./resources/buildConfigDefinitions.js && prettier --write 'src/Options/*.js'",
+ "docs": "jsdoc -c ./jsdoc-conf.json",
+ "lint": "eslint --cache ./ --flag unstable_config_lookup_from_file",
+ "lint-fix": "eslint --fix --cache ./ --flag unstable_config_lookup_from_file",
+ "build": "babel src/ -d lib/ --copy-files --extensions '.ts,.js'",
+ "build:types": "tsc",
+ "watch": "babel --watch src/ -d lib/ --copy-files",
+ "watch:ts": "tsc --watch",
+ "test:mongodb:7.0.16": "MONGODB_VERSION=7.0.16 npm run test",
+ "test:mongodb:8.0.4": "MONGODB_VERSION=8.0.4 npm run test",
+ "test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly",
+ "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
+ "test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner exec -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017 -- npm run testonly",
+ "test:types": "eslint types/tests.ts -c ./types/eslint.config.mjs",
+ "coverage:mongodb": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner exec -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017 -- npm run coverage",
+ "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine",
"start": "node ./bin/parse-server",
- "prepublish": "npm run build"
+ "prettier": "prettier --write {src,spec}/{**/*,*}.js",
+ "prepare": "npm run build",
+ "postinstall": "node -p 'require(\"./postinstall.js\")()'",
+ "madge:circular": "node_modules/.bin/madge ./src --circular",
+ "benchmark": "cross-env MONGODB_VERSION=8.0.4 MONGODB_TOPOLOGY=standalone mongodb-runner exec -t standalone --version 8.0.4 -- --port 27017 -- npm run benchmark:only",
+ "benchmark:only": "node --expose-gc --max-old-space-size=1024 benchmark/performance.js",
+ "benchmark:quick": "cross-env BENCHMARK_ITERATIONS=10 npm run benchmark:only"
},
+ "types": "types/index.d.ts",
"engines": {
- "node": ">=4.3"
+ "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.11.0 <25.0.0"
},
"bin": {
- "parse-server": "./bin/parse-server"
+ "parse-server": "bin/parse-server"
+ },
+ "optionalDependencies": {
+ "@node-rs/bcrypt": "1.10.7"
+ },
+ "collective": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parse-server",
+ "logo": "https://opencollective.com/parse-server/logo.txt?reverse=true&variant=binary"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parse-server"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "{src,spec}/{**/*,*}.js": [
+ "prettier --write",
+ "eslint --fix --cache",
+ "git add"
+ ]
}
}
diff --git a/postinstall.js b/postinstall.js
new file mode 100644
index 0000000000..409ad04e05
--- /dev/null
+++ b/postinstall.js
@@ -0,0 +1,38 @@
+const message = `
+ 1111111111
+ 1111111111111111
+ 1111111111111111111111
+ 11111111111111111111111111
+ 111111111111111 11111111
+ 1111111111111 111 111111
+ 1111111111111 111111111 111111
+ 111111111111 11111111111 111111
+ 1111111111111 11111111111 111111
+ 1111111111111 1111111111 111111
+ 1111111111111111111111111 1111111
+ 11111111 11111111
+ 111111 111 1111111111111111111
+ 11111 11111 111111111111111111
+ 11111 1 11111111111111111
+ 111111 111111111111111111
+ 11111111111111111111111111
+ 1111111111111111111111
+ 111111111111111111
+ 11111111111
+
+ Thank you for using Parse Platform!
+ https://parseplatform.org
+
+Please consider donating to help us maintain
+ this package:
+
+đ https://opencollective.com/parse-server đ
+
+`;
+
+function main() {
+ process.stdout.write(message);
+ process.exit(0);
+}
+
+module.exports = main;
diff --git a/public/custom_json.html b/public/custom_json.html
new file mode 100644
index 0000000000..7e280bfc05
--- /dev/null
+++ b/public/custom_json.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ {{title}}
+
+
+
+ {{heading}}
+ {{body}}
+
+
+
diff --git a/public/custom_json.json b/public/custom_json.json
new file mode 100644
index 0000000000..06d78f1d9d
--- /dev/null
+++ b/public/custom_json.json
@@ -0,0 +1,23 @@
+{
+ "en": {
+ "translation": {
+ "title": "Hello!",
+ "heading": "Welcome to {{appName}}!",
+ "body": "We are delighted to welcome you on board."
+ }
+ },
+ "de": {
+ "translation": {
+ "title": "Hallo!",
+ "heading": "Willkommen bei {{appName}}!",
+ "body": "Wir freuen uns, dich begrÃŧÃen zu dÃŧrfen."
+ }
+ },
+ "de-AT": {
+ "translation": {
+ "title": "Servus!",
+ "heading": "Willkommen bei {{appName}}!",
+ "body": "Wir freuen uns, dich begrÃŧÃen zu dÃŧrfen."
+ }
+ }
+}
\ No newline at end of file
diff --git a/public/custom_page.html b/public/custom_page.html
new file mode 100644
index 0000000000..08a2b3e63c
--- /dev/null
+++ b/public/custom_page.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+ {{appName}}
+
+
+
+ {{appName}}
+
+
+
diff --git a/public/de-AT/email_verification_link_expired.html b/public/de-AT/email_verification_link_expired.html
new file mode 100644
index 0000000000..6a664c48cd
--- /dev/null
+++ b/public/de-AT/email_verification_link_expired.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Expired verification link!
+
+
+
+
diff --git a/public/de-AT/email_verification_link_invalid.html b/public/de-AT/email_verification_link_invalid.html
new file mode 100644
index 0000000000..3a99265a66
--- /dev/null
+++ b/public/de-AT/email_verification_link_invalid.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid verification link!
+
+
+
diff --git a/public/de-AT/email_verification_send_fail.html b/public/de-AT/email_verification_send_fail.html
new file mode 100644
index 0000000000..afd59407b8
--- /dev/null
+++ b/public/de-AT/email_verification_send_fail.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid link!
+ No link sent. User not found or email already verified.
+
+
+
diff --git a/public/de-AT/email_verification_send_success.html b/public/de-AT/email_verification_send_success.html
new file mode 100644
index 0000000000..192a33142b
--- /dev/null
+++ b/public/de-AT/email_verification_send_success.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Link sent!
+ A new link has been sent. Check your email.
+
+
+
diff --git a/public/de-AT/email_verification_success.html b/public/de-AT/email_verification_success.html
new file mode 100644
index 0000000000..e8db182551
--- /dev/null
+++ b/public/de-AT/email_verification_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Email verified!
+ Successfully verified your email for account: {{username}}.
+
+
+
diff --git a/public/de-AT/password_reset.html b/public/de-AT/password_reset.html
new file mode 100644
index 0000000000..73cb1e3d52
--- /dev/null
+++ b/public/de-AT/password_reset.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+Password Reset
+
+
+
+ {{appName}}
+ Reset Your Password
+
+ You can set a new Password for your account: {{username}}
+
+ {{error}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/de-AT/password_reset_link_invalid.html b/public/de-AT/password_reset_link_invalid.html
new file mode 100644
index 0000000000..5db34de15e
--- /dev/null
+++ b/public/de-AT/password_reset_link_invalid.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Invalid password reset link!
+
+
+
diff --git a/public/de-AT/password_reset_success.html b/public/de-AT/password_reset_success.html
new file mode 100644
index 0000000000..4b4e4c7104
--- /dev/null
+++ b/public/de-AT/password_reset_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Success!
+ Your password has been updated.
+
+
+
diff --git a/public/de/email_verification_link_expired.html b/public/de/email_verification_link_expired.html
new file mode 100644
index 0000000000..6a664c48cd
--- /dev/null
+++ b/public/de/email_verification_link_expired.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Expired verification link!
+
+
+
+
diff --git a/public/de/email_verification_link_invalid.html b/public/de/email_verification_link_invalid.html
new file mode 100644
index 0000000000..3a99265a66
--- /dev/null
+++ b/public/de/email_verification_link_invalid.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid verification link!
+
+
+
diff --git a/public/de/email_verification_send_fail.html b/public/de/email_verification_send_fail.html
new file mode 100644
index 0000000000..afd59407b8
--- /dev/null
+++ b/public/de/email_verification_send_fail.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid link!
+ No link sent. User not found or email already verified.
+
+
+
diff --git a/public/de/email_verification_send_success.html b/public/de/email_verification_send_success.html
new file mode 100644
index 0000000000..192a33142b
--- /dev/null
+++ b/public/de/email_verification_send_success.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Link sent!
+ A new link has been sent. Check your email.
+
+
+
diff --git a/public/de/email_verification_success.html b/public/de/email_verification_success.html
new file mode 100644
index 0000000000..e8db182551
--- /dev/null
+++ b/public/de/email_verification_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Email verified!
+ Successfully verified your email for account: {{username}}.
+
+
+
diff --git a/public/de/password_reset.html b/public/de/password_reset.html
new file mode 100644
index 0000000000..73cb1e3d52
--- /dev/null
+++ b/public/de/password_reset.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+Password Reset
+
+
+
+ {{appName}}
+ Reset Your Password
+
+ You can set a new Password for your account: {{username}}
+
+ {{error}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/de/password_reset_link_invalid.html b/public/de/password_reset_link_invalid.html
new file mode 100644
index 0000000000..5db34de15e
--- /dev/null
+++ b/public/de/password_reset_link_invalid.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Invalid password reset link!
+
+
+
diff --git a/public/de/password_reset_success.html b/public/de/password_reset_success.html
new file mode 100644
index 0000000000..4b4e4c7104
--- /dev/null
+++ b/public/de/password_reset_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Success!
+ Your password has been updated.
+
+
+
diff --git a/public/email_verification_link_expired.html b/public/email_verification_link_expired.html
new file mode 100644
index 0000000000..6a664c48cd
--- /dev/null
+++ b/public/email_verification_link_expired.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Expired verification link!
+
+
+
+
diff --git a/public/email_verification_link_invalid.html b/public/email_verification_link_invalid.html
new file mode 100644
index 0000000000..3a99265a66
--- /dev/null
+++ b/public/email_verification_link_invalid.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid verification link!
+
+
+
diff --git a/public/email_verification_send_fail.html b/public/email_verification_send_fail.html
new file mode 100644
index 0000000000..afd59407b8
--- /dev/null
+++ b/public/email_verification_send_fail.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Invalid link!
+ No link sent. User not found or email already verified.
+
+
+
diff --git a/public/email_verification_send_success.html b/public/email_verification_send_success.html
new file mode 100644
index 0000000000..192a33142b
--- /dev/null
+++ b/public/email_verification_send_success.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Link sent!
+ A new link has been sent. Check your email.
+
+
+
diff --git a/public/email_verification_success.html b/public/email_verification_success.html
new file mode 100644
index 0000000000..e8db182551
--- /dev/null
+++ b/public/email_verification_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Email Verification
+
+
+
+ {{appName}}
+ Email verified!
+ Successfully verified your email for account: {{username}}.
+
+
+
diff --git a/public/password_reset.html b/public/password_reset.html
new file mode 100644
index 0000000000..73cb1e3d52
--- /dev/null
+++ b/public/password_reset.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+Password Reset
+
+
+
+ {{appName}}
+ Reset Your Password
+
+ You can set a new Password for your account: {{username}}
+
+ {{error}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/password_reset_link_invalid.html b/public/password_reset_link_invalid.html
new file mode 100644
index 0000000000..5db34de15e
--- /dev/null
+++ b/public/password_reset_link_invalid.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Invalid password reset link!
+
+
+
diff --git a/public/password_reset_success.html b/public/password_reset_success.html
new file mode 100644
index 0000000000..4b4e4c7104
--- /dev/null
+++ b/public/password_reset_success.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Password Reset
+
+
+
+ {{appName}}
+ Success!
+ Your password has been updated.
+
+
+
diff --git a/public_html/invalid_link.html b/public_html/invalid_link.html
deleted file mode 100644
index 66bdc788fb..0000000000
--- a/public_html/invalid_link.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
- Invalid Link
-
-
-
-
Invalid Link
-
-
-
diff --git a/public_html/password_reset_success.html b/public_html/password_reset_success.html
deleted file mode 100644
index 774cbb350c..0000000000
--- a/public_html/password_reset_success.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
- Password Reset
-
-
- Successfully updated your password!
-
-
diff --git a/public_html/verify_email_success.html b/public_html/verify_email_success.html
deleted file mode 100644
index 774ea38a0d..0000000000
--- a/public_html/verify_email_success.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
- Email Verification
-
-
- Successfully verified your email!
-
-
diff --git a/release_docs.sh b/release_docs.sh
new file mode 100755
index 0000000000..0c7cc2b395
--- /dev/null
+++ b/release_docs.sh
@@ -0,0 +1,41 @@
+#!/bin/sh -e
+set -x
+# GITHUB_ACTIONS=true SOURCE_TAG=test ./release_docs.sh
+
+if [ "${GITHUB_ACTIONS}" = "" ];
+then
+ echo "Cannot release docs without GITHUB_ACTIONS set"
+ exit 0;
+fi
+if [ "${SOURCE_TAG}" = "" ];
+then
+ echo "Cannot release docs without SOURCE_TAG set"
+ exit 0;
+fi
+REPO="https://github.com/parse-community/parse-server"
+
+rm -rf docs
+git clone -b gh-pages --single-branch $REPO ./docs
+cd docs
+git pull origin gh-pages
+cd ..
+
+RELEASE="release"
+VERSION="${SOURCE_TAG}"
+
+# change the default page to the latest
+echo "" > "docs/api/index.html"
+
+npm run definitions
+npm run docs
+
+mkdir -p "docs/api/${RELEASE}"
+cp -R out/* "docs/api/${RELEASE}"
+
+mkdir -p "docs/api/${VERSION}"
+cp -R out/* "docs/api/${VERSION}"
+
+# Copy other resources
+RESOURCE_DIR=".github"
+mkdir -p "docs/${RESOURCE_DIR}"
+cp "./.github/parse-server-logo.png" "docs/${RESOURCE_DIR}/"
diff --git a/resources/buildConfigDefinitions.js b/resources/buildConfigDefinitions.js
new file mode 100644
index 0000000000..1a3cbb4b55
--- /dev/null
+++ b/resources/buildConfigDefinitions.js
@@ -0,0 +1,405 @@
+/**
+ * Parse Server Configuration Builder
+ *
+ * This module builds the definitions file (src/Options/Definitions.js)
+ * from the src/Options/index.js options interfaces.
+ * The Definitions.js module is responsible for the default values as well
+ * as the mappings for the CLI.
+ *
+ * To rebuild the definitions file, run
+ * `$ node resources/buildConfigDefinitions.js`
+ */
+const parsers = require('../src/Options/parsers');
+
+/** The types of nested options. */
+const nestedOptionTypes = [
+ 'CustomPagesOptions',
+ 'DatabaseOptions',
+ 'FileDownloadOptions',
+ 'FileUploadOptions',
+ 'IdempotencyOptions',
+ 'InstallationOptions',
+ 'Object',
+ 'PagesCustomUrlsOptions',
+ 'PagesOptions',
+ 'PagesRoute',
+ 'PasswordPolicyOptions',
+ 'QueryServerOptions',
+ 'RequestComplexityOptions',
+ 'SecurityOptions',
+ 'SchemaOptions',
+ 'LogLevels',
+];
+
+/** The prefix of environment variables for nested options. */
+const nestedOptionEnvPrefix = {
+ AccountLockoutOptions: 'PARSE_SERVER_ACCOUNT_LOCKOUT_',
+ DatabaseOptionsClientMetadata: 'PARSE_SERVER_DATABASE_CLIENT_METADATA_',
+ CustomPagesOptions: 'PARSE_SERVER_CUSTOM_PAGES_',
+ DatabaseOptions: 'PARSE_SERVER_DATABASE_',
+ FileDownloadOptions: 'PARSE_SERVER_FILE_DOWNLOAD_',
+ FileUploadOptions: 'PARSE_SERVER_FILE_UPLOAD_',
+ IdempotencyOptions: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_',
+ InstallationOptions: 'PARSE_SERVER_INSTALLATION_',
+ LiveQueryOptions: 'PARSE_SERVER_LIVEQUERY_',
+ LiveQueryServerOptions: 'PARSE_LIVE_QUERY_SERVER_',
+ LogClientEvent: 'PARSE_SERVER_DATABASE_LOG_CLIENT_EVENTS_',
+ LogLevel: 'PARSE_SERVER_LOG_LEVEL_',
+ LogLevels: 'PARSE_SERVER_LOG_LEVELS_',
+ PagesCustomUrlsOptions: 'PARSE_SERVER_PAGES_CUSTOM_URL_',
+ PagesOptions: 'PARSE_SERVER_PAGES_',
+ PagesRoute: 'PARSE_SERVER_PAGES_ROUTE_',
+ ParseServerOptions: 'PARSE_SERVER_',
+ PasswordPolicyOptions: 'PARSE_SERVER_PASSWORD_POLICY_',
+ QueryServerOptions: 'PARSE_SERVER_QUERY_',
+ RateLimitOptions: 'PARSE_SERVER_RATE_LIMIT_',
+ RequestComplexityOptions: 'PARSE_SERVER_REQUEST_COMPLEXITY_',
+ SchemaOptions: 'PARSE_SERVER_SCHEMA_',
+ SecurityOptions: 'PARSE_SERVER_SECURITY_',
+};
+
+function last(array) {
+ return array[array.length - 1];
+}
+
+const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+function toENV(key) {
+ let str = '';
+ let previousIsUpper = false;
+ for (let i = 0; i < key.length; i++) {
+ const char = key[i];
+ if (letters.indexOf(char) >= 0) {
+ if (!previousIsUpper) {
+ str += '_';
+ previousIsUpper = true;
+ }
+ } else {
+ previousIsUpper = false;
+ }
+ str += char;
+ }
+ return str.toUpperCase();
+}
+
+function getCommentValue(comment) {
+ if (!comment) {
+ return;
+ }
+ return comment.value.trim();
+}
+
+function getENVPrefix(iface) {
+ if (nestedOptionEnvPrefix[iface.id.name]) {
+ return nestedOptionEnvPrefix[iface.id.name];
+ }
+}
+
+function processProperty(property, iface) {
+ const firstComment = getCommentValue(last(property.leadingComments || []));
+ const name = property.key.name;
+ const prefix = getENVPrefix(iface);
+
+ if (!firstComment) {
+ return;
+ }
+ const lines = firstComment.split('\n').map(line => line.trim());
+ let help = '';
+ let envLine;
+ let defaultLine;
+ lines.forEach(line => {
+ if (line.indexOf(':ENV:') === 0) {
+ envLine = line;
+ } else if (line.indexOf(':DEFAULT:') === 0) {
+ defaultLine = line;
+ } else {
+ help += line;
+ }
+ });
+ let env;
+ if (envLine) {
+ env = envLine.split(' ')[1];
+ } else {
+ env = prefix + toENV(name);
+ }
+ let defaultValue;
+ if (defaultLine) {
+ const defaultArray = defaultLine.split(' ');
+ defaultArray.shift();
+ defaultValue = defaultArray.join(' ');
+ }
+ let type = property.value.type;
+ let isRequired = true;
+ if (type == 'NullableTypeAnnotation') {
+ isRequired = false;
+ type = property.value.typeAnnotation.type;
+ }
+ return {
+ name,
+ env,
+ help,
+ type,
+ defaultValue,
+ types: property.value.types,
+ typeAnnotation: property.value.typeAnnotation,
+ required: isRequired,
+ };
+}
+
+function doInterface(iface) {
+ return iface.body.properties
+ .sort((a, b) => a.key.name.localeCompare(b.key.name))
+ .map(prop => processProperty(prop, iface))
+ .filter(e => e !== undefined);
+}
+
+function mapperFor(elt, t) {
+ const p = t.identifier('parsers');
+ const wrap = identifier => t.memberExpression(p, identifier);
+
+ if (t.isNumberTypeAnnotation(elt)) {
+ return t.callExpression(wrap(t.identifier('numberParser')), [t.stringLiteral(elt.name)]);
+ } else if (t.isArrayTypeAnnotation(elt)) {
+ return wrap(t.identifier('arrayParser'));
+ } else if (t.isAnyTypeAnnotation(elt)) {
+ return wrap(t.identifier('objectParser'));
+ } else if (t.isBooleanTypeAnnotation(elt)) {
+ return wrap(t.identifier('booleanParser'));
+ } else if (t.isObjectTypeAnnotation(elt)) {
+ return wrap(t.identifier('objectParser'));
+ } else if (t.isUnionTypeAnnotation(elt)) {
+ const unionTypes = elt.typeAnnotation?.types || elt.types;
+ if (unionTypes?.some(type => t.isBooleanTypeAnnotation(type)) && unionTypes?.some(type => t.isFunctionTypeAnnotation(type))) {
+ return wrap(t.identifier('booleanOrFunctionParser'));
+ }
+ } else if (t.isGenericTypeAnnotation(elt)) {
+ const type = elt.typeAnnotation.id.name;
+ if (type == 'Adapter') {
+ return wrap(t.identifier('moduleOrObjectParser'));
+ }
+ if (type == 'NumberOrBoolean') {
+ return wrap(t.identifier('numberOrBooleanParser'));
+ }
+ if (type == 'NumberOrString') {
+ return t.callExpression(wrap(t.identifier('numberOrStringParser')), [t.stringLiteral(elt.name)]);
+ }
+ if (type === 'StringOrStringArray') {
+ return wrap(t.identifier('arrayParser'));
+ }
+ return wrap(t.identifier('objectParser'));
+ }
+}
+
+function parseDefaultValue(elt, value, t) {
+ let literalValue;
+ if (t.isStringTypeAnnotation(elt)) {
+ if (value == '""' || value == "''") {
+ literalValue = t.stringLiteral('');
+ } else {
+ literalValue = t.stringLiteral(value);
+ }
+ } else if (t.isNumberTypeAnnotation(elt)) {
+ literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
+ } else if (t.isArrayTypeAnnotation(elt)) {
+ const array = parsers.objectParser(value);
+ literalValue = t.arrayExpression(
+ array.map(value => {
+ if (typeof value == 'string') {
+ return t.stringLiteral(value);
+ } else if (typeof value == 'number') {
+ return t.numericLiteral(value);
+ } else if (typeof value == 'object') {
+ const object = parsers.objectParser(value);
+ const props = Object.entries(object).map(([k, v]) => {
+ if (typeof v == 'string') {
+ return t.objectProperty(t.identifier(k), t.stringLiteral(v));
+ } else if (typeof v == 'number') {
+ return t.objectProperty(t.identifier(k), t.numericLiteral(v));
+ } else if (typeof v == 'boolean') {
+ return t.objectProperty(t.identifier(k), t.booleanLiteral(v));
+ }
+ });
+ return t.objectExpression(props);
+ } else {
+ throw new Error('Unable to parse array');
+ }
+ })
+ );
+ } else if (t.isAnyTypeAnnotation(elt)) {
+ literalValue = t.arrayExpression([]);
+ } else if (t.isBooleanTypeAnnotation(elt)) {
+ literalValue = t.booleanLiteral(parsers.booleanParser(value));
+ } else if (t.isGenericTypeAnnotation(elt)) {
+ const type = elt.typeAnnotation.id.name;
+ if (type == 'NumberOrBoolean') {
+ literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
+ }
+ if (type == 'NumberOrString') {
+ literalValue = t.numericLiteral(parsers.numberOrStringParser('')(value));
+ }
+
+ if (nestedOptionTypes.includes(type)) {
+ const object = parsers.objectParser(value);
+ const props = Object.keys(object).map(key => {
+ return t.objectProperty(key, object[value]);
+ });
+ literalValue = t.objectExpression(props);
+ }
+ if (type == 'ProtectedFields') {
+ const prop = t.objectProperty(
+ t.stringLiteral('_User'),
+ t.objectPattern([
+ t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')])),
+ ])
+ );
+ literalValue = t.objectExpression([prop]);
+ }
+ }
+ return literalValue;
+}
+
+function inject(t, list) {
+ let comments = '';
+ const results = list
+ .map(elt => {
+ if (!elt.name) {
+ return;
+ }
+ const props = ['env', 'help']
+ .map(key => {
+ if (elt[key]) {
+ return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key]));
+ }
+ })
+ .filter(e => e !== undefined);
+ if (elt.required) {
+ props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true)));
+ }
+ const action = mapperFor(elt, t);
+ if (action) {
+ props.push(t.objectProperty(t.stringLiteral('action'), action));
+ }
+
+ if (t.isGenericTypeAnnotation(elt)) {
+ if (elt.typeAnnotation.id.name in nestedOptionEnvPrefix) {
+ props.push(
+ t.objectProperty(t.stringLiteral('type'), t.stringLiteral(elt.typeAnnotation.id.name))
+ );
+ }
+ } else if (t.isArrayTypeAnnotation(elt)) {
+ const elementType = elt.typeAnnotation.elementType;
+ if (t.isGenericTypeAnnotation(elementType)) {
+ if (elementType.id.name in nestedOptionEnvPrefix) {
+ props.push(
+ t.objectProperty(t.stringLiteral('type'), t.stringLiteral(elementType.id.name + '[]'))
+ );
+ }
+ }
+ }
+ if (elt.defaultValue) {
+ let parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
+ if (!parsedValue) {
+ for (const type of elt.typeAnnotation.types) {
+ elt.type = type.type;
+ parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
+ if (parsedValue) {
+ break;
+ }
+ }
+ }
+ if (parsedValue) {
+ props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
+ } else {
+ throw new Error(`Unable to parse value for ${elt.name} `);
+ }
+ }
+ let type = elt.type.replace('TypeAnnotation', '');
+ if (type === 'Generic') {
+ type = elt.typeAnnotation.id.name;
+ }
+ if (type === 'Array') {
+ type = elt.typeAnnotation.elementType.id
+ ? `${elt.typeAnnotation.elementType.id.name}[]`
+ : `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`;
+ }
+ if (type === 'NumberOrBoolean') {
+ type = 'Number|Boolean';
+ }
+ if (type === 'NumberOrString') {
+ type = 'Number|String';
+ }
+ if (type === 'Adapter') {
+ const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
+ type = `Adapter<${adapterType}>`;
+ }
+ if (type === 'StringOrStringArray') {
+ type = 'String|String[]';
+ }
+ comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
+ const obj = t.objectExpression(props);
+ return t.objectProperty(t.stringLiteral(elt.name), obj);
+ })
+ .filter(elt => {
+ return elt != undefined;
+ });
+ return { results, comments };
+}
+
+const makeRequire = function (variableName, module, t) {
+ const decl = t.variableDeclarator(
+ t.identifier(variableName),
+ t.callExpression(t.identifier('require'), [t.stringLiteral(module)])
+ );
+ return t.variableDeclaration('var', [decl]);
+};
+let docs = ``;
+const plugin = function (babel) {
+ const t = babel.types;
+ const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports'));
+ return {
+ visitor: {
+ ImportDeclaration: function (path) {
+ path.remove();
+ },
+ Program: function (path) {
+ // Inject the parser's loader
+ path.unshiftContainer('body', makeRequire('parsers', './parsers', t));
+ },
+ ExportDeclaration: function (path) {
+ // Export declaration on an interface
+ if (
+ path.node &&
+ path.node.declaration &&
+ path.node.declaration.type == 'InterfaceDeclaration'
+ ) {
+ const { results, comments } = inject(t, doInterface(path.node.declaration));
+ const id = path.node.declaration.id.name;
+ const exports = t.memberExpression(moduleExports, t.identifier(id));
+ docs += `/**\n * @interface ${id}\n${comments} */\n\n`;
+ path.replaceWith(t.assignmentExpression('=', exports, t.objectExpression(results)));
+ }
+ },
+ },
+ };
+};
+
+const auxiliaryCommentBefore = `
+**** GENERATED CODE ****
+This code has been generated by resources/buildConfigDefinitions.js
+Do not edit manually, but update Options/index.js
+`;
+
+// Only run the transformation when executed directly, not when imported by tests
+if (require.main === module) {
+ const babel = require('@babel/core');
+ const res = babel.transformFileSync('./src/Options/index.js', {
+ plugins: [plugin, '@babel/transform-flow-strip-types'],
+ babelrc: false,
+ auxiliaryCommentBefore,
+ sourceMaps: false,
+ });
+ require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
+ require('fs').writeFileSync('./src/Options/docs.js', docs);
+}
+
+// Export mapperFor for testing
+module.exports = { mapperFor };
diff --git a/scripts/before_script_postgres.sh b/scripts/before_script_postgres.sh
new file mode 100755
index 0000000000..5c445c4df1
--- /dev/null
+++ b/scripts/before_script_postgres.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+echo "[SCRIPT] Before Script :: Setup Parse DB for Postgres"
+
+PGPASSWORD=postgres psql -v ON_ERROR_STOP=1 -h localhost -U postgres <<-EOSQL
+ CREATE DATABASE parse_server_postgres_adapter_test_database;
+ \c parse_server_postgres_adapter_test_database;
+ CREATE EXTENSION pgcrypto;
+ CREATE EXTENSION postgis;
+ CREATE EXTENSION postgis_topology;
+EOSQL
diff --git a/scripts/before_script_postgres_conf.sh b/scripts/before_script_postgres_conf.sh
new file mode 100755
index 0000000000..ec471d9c3f
--- /dev/null
+++ b/scripts/before_script_postgres_conf.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+
+echo "[SCRIPT] Before Script :: Setup Parse Postgres configuration file"
+
+# DB Version: 13
+# OS Type: linux
+# DB Type: web
+# Total Memory (RAM): 6 GB
+# CPUs num: 1
+# Data Storage: ssd
+
+PGPASSWORD=postgres psql -v ON_ERROR_STOP=1 -h localhost -U postgres <<-EOSQL
+ ALTER SYSTEM SET max_connections TO '200';
+ ALTER SYSTEM SET shared_buffers TO '1536MB';
+ ALTER SYSTEM SET effective_cache_size TO '4608MB';
+ ALTER SYSTEM SET maintenance_work_mem TO '384MB';
+ ALTER SYSTEM SET checkpoint_completion_target TO '0.9';
+ ALTER SYSTEM SET wal_buffers TO '16MB';
+ ALTER SYSTEM SET default_statistics_target TO '100';
+ ALTER SYSTEM SET random_page_cost TO '1.1';
+ ALTER SYSTEM SET effective_io_concurrency TO '200';
+ ALTER SYSTEM SET work_mem TO '3932kB';
+ ALTER SYSTEM SET min_wal_size TO '1GB';
+ ALTER SYSTEM SET max_wal_size TO '4GB';
+ SELECT pg_reload_conf();
+EOSQL
+
+exec "$@"
diff --git a/spec/.babelrc b/spec/.babelrc
new file mode 100644
index 0000000000..633eaf7fac
--- /dev/null
+++ b/spec/.babelrc
@@ -0,0 +1,14 @@
+{
+ "plugins": [
+ "@babel/plugin-proposal-object-rest-spread"
+ ],
+ "presets": [
+ "@babel/preset-typescript",
+ ["@babel/preset-env", {
+ "targets": {
+ "node": "18"
+ }
+ }]
+ ],
+ "sourceMaps": "inline"
+}
diff --git a/spec/AccountLockoutPolicy.spec.js b/spec/AccountLockoutPolicy.spec.js
new file mode 100644
index 0000000000..91d30e55fa
--- /dev/null
+++ b/spec/AccountLockoutPolicy.spec.js
@@ -0,0 +1,516 @@
+'use strict';
+
+const Config = require('../lib/Config');
+const Definitions = require('../lib/Options/Definitions');
+const request = require('../lib/request');
+
+const loginWithWrongCredentialsShouldFail = function (username, password) {
+ return new Promise((resolve, reject) => {
+ Parse.User.logIn(username, password)
+ .then(() => reject('login should have failed'))
+ .catch(err => {
+ if (err.message === 'Invalid username/password.') {
+ resolve();
+ } else {
+ reject(err);
+ }
+ });
+ });
+};
+
+const isAccountLockoutError = function (username, password, duration, waitTime) {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ Parse.User.logIn(username, password)
+ .then(() => reject('login should have failed'))
+ .catch(err => {
+ if (
+ err.message ===
+ 'Your account is locked due to multiple failed login attempts. Please try again after ' +
+ duration +
+ ' minute(s)'
+ ) {
+ resolve();
+ } else {
+ reject(err);
+ }
+ });
+ }, waitTime);
+ });
+};
+
+describe('Account Lockout Policy: ', () => {
+ it('account should not be locked even after failed login attempts if account lockout policy is not set', done => {
+ reconfigureServer({
+ appName: 'unlimited',
+ publicServerURL: 'http://localhost:1337/1',
+ })
+ .then(() => {
+ const user = new Parse.User();
+ user.setUsername('username1');
+ user.setPassword('password');
+ return user.signUp(null);
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 1');
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 2');
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 3');
+ })
+ .then(() => done())
+ .catch(err => {
+ fail('allow unlimited failed login attempts failed: ' + JSON.stringify(err));
+ done();
+ });
+ });
+
+ it('throw error if duration is set to an invalid number', done => {
+ reconfigureServer({
+ appName: 'duration',
+ accountLockout: {
+ duration: 'invalid value',
+ threshold: 5,
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('set duration to an invalid number test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout duration should be greater than 0 and less than 100000'
+ ) {
+ done();
+ } else {
+ fail('set duration to an invalid number test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('throw error if threshold is set to an invalid number', done => {
+ reconfigureServer({
+ appName: 'threshold',
+ accountLockout: {
+ duration: 5,
+ threshold: 'invalid number',
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('set threshold to an invalid number test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout threshold should be an integer greater than 0 and less than 1000'
+ ) {
+ done();
+ } else {
+ fail('set threshold to an invalid number test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('throw error if threshold is < 1', done => {
+ reconfigureServer({
+ appName: 'threshold',
+ accountLockout: {
+ duration: 5,
+ threshold: 0,
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('threshold value < 1 is invalid test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout threshold should be an integer greater than 0 and less than 1000'
+ ) {
+ done();
+ } else {
+ fail('threshold value < 1 is invalid test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('throw error if threshold is > 999', done => {
+ reconfigureServer({
+ appName: 'threshold',
+ accountLockout: {
+ duration: 5,
+ threshold: 1000,
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('threshold value > 999 is invalid test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout threshold should be an integer greater than 0 and less than 1000'
+ ) {
+ done();
+ } else {
+ fail('threshold value > 999 is invalid test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('throw error if duration is <= 0', done => {
+ reconfigureServer({
+ appName: 'duration',
+ accountLockout: {
+ duration: 0,
+ threshold: 5,
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('duration value < 1 is invalid test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout duration should be greater than 0 and less than 100000'
+ ) {
+ done();
+ } else {
+ fail('duration value < 1 is invalid test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('throw error if duration is > 99999', done => {
+ reconfigureServer({
+ appName: 'duration',
+ accountLockout: {
+ duration: 100000,
+ threshold: 5,
+ },
+ publicServerURL: 'https://my.public.server.com/1',
+ })
+ .then(() => {
+ Config.get('test');
+ fail('duration value > 99999 is invalid test failed');
+ done();
+ })
+ .catch(err => {
+ if (
+ err &&
+ err === 'Account lockout duration should be greater than 0 and less than 100000'
+ ) {
+ done();
+ } else {
+ fail('duration value > 99999 is invalid test failed: ' + JSON.stringify(err));
+ done();
+ }
+ });
+ });
+
+ it('lock account if failed login attempts are above threshold', done => {
+ reconfigureServer({
+ appName: 'lockout threshold',
+ accountLockout: {
+ duration: 1,
+ threshold: 2,
+ },
+ publicServerURL: 'http://localhost:8378/1',
+ })
+ .then(() => {
+ const user = new Parse.User();
+ user.setUsername('username2');
+ user.setPassword('failedLoginAttemptsThreshold');
+ return user.signUp();
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username2', 'wrong password');
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username2', 'wrong password');
+ })
+ .then(() => {
+ return isAccountLockoutError('username2', 'wrong password', 1, 1);
+ })
+ .then(() => {
+ done();
+ })
+ .catch(err => {
+ fail('lock account after failed login attempts test failed: ' + JSON.stringify(err));
+ done();
+ });
+ });
+
+ it('lock account for accountPolicy.duration minutes if failed login attempts are above threshold', done => {
+ reconfigureServer({
+ appName: 'lockout threshold',
+ accountLockout: {
+ duration: 0.05, // 0.05*60 = 3 secs
+ threshold: 2,
+ },
+ publicServerURL: 'http://localhost:8378/1',
+ })
+ .then(() => {
+ const user = new Parse.User();
+ user.setUsername('username3');
+ user.setPassword('failedLoginAttemptsThreshold');
+ return user.signUp();
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username3', 'wrong password');
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username3', 'wrong password');
+ })
+ .then(() => {
+ return isAccountLockoutError('username3', 'wrong password', 0.05, 1);
+ })
+ .then(() => {
+ // account should still be locked even after 2 seconds.
+ return isAccountLockoutError('username3', 'wrong password', 0.05, 2000);
+ })
+ .then(() => {
+ done();
+ })
+ .catch(err => {
+ fail('account should be locked for duration mins test failed: ' + JSON.stringify(err));
+ done();
+ });
+ });
+
+ it('allow login for locked account after accountPolicy.duration minutes', done => {
+ reconfigureServer({
+ appName: 'lockout threshold',
+ accountLockout: {
+ duration: 0.05, // 0.05*60 = 3 secs
+ threshold: 2,
+ },
+ publicServerURL: 'http://localhost:8378/1',
+ })
+ .then(() => {
+ const user = new Parse.User();
+ user.setUsername('username4');
+ user.setPassword('correct password');
+ return user.signUp();
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username4', 'wrong password');
+ })
+ .then(() => {
+ return loginWithWrongCredentialsShouldFail('username4', 'wrong password');
+ })
+ .then(() => {
+ // allow locked user to login after 3 seconds with a valid userid and password
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ Parse.User.logIn('username4', 'correct password')
+ .then(() => resolve())
+ .catch(err => reject(err));
+ }, 3001);
+ });
+ })
+ .then(() => {
+ done();
+ })
+ .catch(err => {
+ fail(
+ 'allow login for locked account after accountPolicy.duration minutes test failed: ' +
+ JSON.stringify(err)
+ );
+ done();
+ });
+ });
+
+ it('should enforce lockout threshold under concurrent failed login attempts', async () => {
+ const threshold = 3;
+ await reconfigureServer({
+ appName: 'lockout race',
+ accountLockout: {
+ duration: 5,
+ threshold,
+ },
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ const user = new Parse.User();
+ user.setUsername('race_user');
+ user.setPassword('correct_password');
+ await user.signUp();
+
+ const concurrency = 30;
+ const results = await Promise.all(
+ Array.from({ length: concurrency }, () =>
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ username: 'race_user', password: 'wrong_password' }),
+ }).catch(err => err)
+ )
+ );
+
+ const lockoutError =
+ 'Your account is locked due to multiple failed login attempts. Please try again after 5 minute(s)';
+ const errors = results.map(r => {
+ const body = typeof r.data === 'string' ? JSON.parse(r.data) : r.data;
+ return body?.error;
+ });
+ const invalidPassword = errors.filter(error => error === 'Invalid username/password.');
+ const lockoutResponses = errors.filter(error => error === lockoutError);
+
+ expect(
+ errors.every(
+ error => error === 'Invalid username/password.' || error === lockoutError
+ )
+ ).toBeTrue();
+ expect(lockoutResponses.length).toBeGreaterThan(0);
+ expect(invalidPassword.length).toBeLessThanOrEqual(threshold);
+ });
+});
+
+describe('lockout with password reset option', () => {
+ let sendPasswordResetEmail;
+
+ async function setup(options = {}) {
+ const accountLockout = Object.assign(
+ {
+ duration: 10000,
+ threshold: 1,
+ },
+ options
+ );
+ const config = {
+ appName: 'exampleApp',
+ accountLockout: accountLockout,
+ publicServerURL: 'http://localhost:8378/1',
+ emailAdapter: {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ },
+ };
+ await reconfigureServer(config);
+
+ sendPasswordResetEmail = spyOn(config.emailAdapter, 'sendPasswordResetEmail').and.callThrough();
+ }
+
+ it('accepts valid unlockOnPasswordReset option', async () => {
+ const values = [true, false];
+
+ for (const value of values) {
+ await expectAsync(setup({ unlockOnPasswordReset: value })).toBeResolved();
+ }
+ });
+
+ it('rejects invalid unlockOnPasswordReset option', async () => {
+ const values = ['a', 0, {}, [], null];
+
+ for (const value of values) {
+ await expectAsync(setup({ unlockOnPasswordReset: value })).toBeRejected();
+ }
+ });
+
+ it('uses default value if unlockOnPasswordReset is not set', async () => {
+ await expectAsync(setup({ unlockOnPasswordReset: undefined })).toBeResolved();
+
+ const parseConfig = Config.get(Parse.applicationId);
+ expect(parseConfig.accountLockout.unlockOnPasswordReset).toBe(
+ Definitions.AccountLockoutOptions.unlockOnPasswordReset.default
+ );
+ });
+
+ it('allow login for locked account after password reset', async () => {
+ await setup({ unlockOnPasswordReset: true });
+ const config = Config.get(Parse.applicationId);
+
+ const user = new Parse.User();
+ const username = 'exampleUsername';
+ const password = 'examplePassword';
+ user.setUsername(username);
+ user.setPassword(password);
+ user.setEmail('mail@example.com');
+ await user.signUp();
+
+ await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected();
+ await expectAsync(Parse.User.logIn(username, password)).toBeRejected();
+
+ await Parse.User.requestPasswordReset(user.getEmail());
+ await expectAsync(Parse.User.logIn(username, password)).toBeRejected();
+
+ const link = sendPasswordResetEmail.calls.all()[0].args[0].link;
+ const linkUrl = new URL(link);
+ const token = linkUrl.searchParams.get('token');
+ const newPassword = 'newPassword';
+ await request({
+ method: 'POST',
+ url: `${config.publicServerURL}/apps/test/request_password_reset`,
+ body: `new_password=${newPassword}&token=${token}`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ followRedirects: false,
+ });
+
+ await expectAsync(Parse.User.logIn(username, newPassword)).toBeResolved();
+ });
+
+ it('reject login for locked account after password reset (default)', async () => {
+ await setup();
+ const config = Config.get(Parse.applicationId);
+
+ const user = new Parse.User();
+ const username = 'exampleUsername';
+ const password = 'examplePassword';
+ user.setUsername(username);
+ user.setPassword(password);
+ user.setEmail('mail@example.com');
+ await user.signUp();
+
+ await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected();
+ await expectAsync(Parse.User.logIn(username, password)).toBeRejected();
+
+ await Parse.User.requestPasswordReset(user.getEmail());
+ await expectAsync(Parse.User.logIn(username, password)).toBeRejected();
+
+ const link = sendPasswordResetEmail.calls.all()[0].args[0].link;
+ const linkUrl = new URL(link);
+ const token = linkUrl.searchParams.get('token');
+ const newPassword = 'newPassword';
+ await request({
+ method: 'POST',
+ url: `${config.publicServerURL}/apps/test/request_password_reset`,
+ body: `new_password=${newPassword}&token=${token}`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ followRedirects: false,
+ });
+
+ await expectAsync(Parse.User.logIn(username, newPassword)).toBeRejected();
+ });
+});
diff --git a/spec/AdaptableController.spec.js b/spec/AdaptableController.spec.js
index a5d5994662..4cda42e162 100644
--- a/spec/AdaptableController.spec.js
+++ b/spec/AdaptableController.spec.js
@@ -1,83 +1,84 @@
+const AdaptableController = require('../lib/Controllers/AdaptableController').AdaptableController;
+const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default;
+const FilesController = require('../lib/Controllers/FilesController').FilesController;
-var AdaptableController = require("../src/Controllers/AdaptableController").AdaptableController;
-var FilesAdapter = require("../src/Adapters/Files/FilesAdapter").default;
-var FilesController = require("../src/Controllers/FilesController").FilesController;
-
-var MockController = function(options) {
+const MockController = function (options) {
AdaptableController.call(this, options);
-}
+};
MockController.prototype = Object.create(AdaptableController.prototype);
MockController.prototype.constructor = AdaptableController;
-describe("AdaptableController", ()=>{
- it("should use the provided adapter", (done) => {
- var adapter = new FilesAdapter();
- var controller = new FilesController(adapter);
+describe('AdaptableController', () => {
+ it('should use the provided adapter', done => {
+ const adapter = new FilesAdapter();
+ const controller = new FilesController(adapter);
expect(controller.adapter).toBe(adapter);
// make sure _adapter is private
expect(controller._adapter).toBe(undefined);
// Override _adapter is not doing anything
- controller._adapter = "Hello";
+ controller._adapter = 'Hello';
expect(controller.adapter).toBe(adapter);
done();
});
- it("should throw when creating a new mock controller", (done) => {
- var adapter = new FilesAdapter();
+ it('should throw when creating a new mock controller', done => {
+ const adapter = new FilesAdapter();
expect(() => {
new MockController(adapter);
}).toThrow();
done();
});
- it("should fail setting the wrong adapter to the controller", (done) => {
- function WrongAdapter() {};
- var adapter = new FilesAdapter();
- var controller = new FilesController(adapter);
- var otherAdapter = new WrongAdapter();
+ it('should fail setting the wrong adapter to the controller', done => {
+ function WrongAdapter() {}
+ const adapter = new FilesAdapter();
+ const controller = new FilesController(adapter);
+ const otherAdapter = new WrongAdapter();
expect(() => {
controller.adapter = otherAdapter;
}).toThrow();
done();
});
- it("should fail to instantiate a controller with wrong adapter", (done) => {
- function WrongAdapter() {};
- var adapter = new WrongAdapter();
+ it('should fail to instantiate a controller with wrong adapter', done => {
+ function WrongAdapter() {}
+ const adapter = new WrongAdapter();
expect(() => {
new FilesController(adapter);
}).toThrow();
done();
});
- it("should fail to instantiate a controller without an adapter", (done) => {
+ it('should fail to instantiate a controller without an adapter', done => {
expect(() => {
new FilesController();
}).toThrow();
done();
});
- it("should accept an object adapter", (done) => {
- var adapter = {
- createFile: function(config, filename, data) { },
- deleteFile: function(config, filename) { },
- getFileData: function(config, filename) { },
- getFileLocation: function(config, filename) { },
- }
+ it('should accept an object adapter', done => {
+ const adapter = {
+ createFile: function () {},
+ deleteFile: function () {},
+ getFileData: function () {},
+ getFileLocation: function () {},
+ validateFilename: function () {},
+ };
expect(() => {
new FilesController(adapter);
}).not.toThrow();
done();
});
- it("should accept an object adapter", (done) => {
- function AGoodAdapter() {};
- AGoodAdapter.prototype.createFile = function(config, filename, data) { };
- AGoodAdapter.prototype.deleteFile = function(config, filename) { };
- AGoodAdapter.prototype.getFileData = function(config, filename) { };
- AGoodAdapter.prototype.getFileLocation = function(config, filename) { };
+ it('should accept an prototype based object adapter', done => {
+ function AGoodAdapter() {}
+ AGoodAdapter.prototype.createFile = function () {};
+ AGoodAdapter.prototype.deleteFile = function () {};
+ AGoodAdapter.prototype.getFileData = function () {};
+ AGoodAdapter.prototype.getFileLocation = function () {};
+ AGoodAdapter.prototype.validateFilename = function () {};
- var adapter = new AGoodAdapter();
+ const adapter = new AGoodAdapter();
expect(() => {
new FilesController(adapter);
}).not.toThrow();
diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js
index 250a8a7c49..8d33bf2094 100644
--- a/spec/AdapterLoader.spec.js
+++ b/spec/AdapterLoader.spec.js
@@ -1,40 +1,39 @@
+const { loadAdapter, loadModule } = require('../lib/Adapters/AdapterLoader');
+const FilesAdapter = require('@parse/fs-files-adapter').default;
+const MockFilesAdapter = require('mock-files-adapter');
+const Config = require('../lib/Config');
+const Utils = require('../lib/Utils');
-var loadAdapter = require("../src/Adapters/AdapterLoader").loadAdapter;
-var FilesAdapter = require("parse-server-fs-adapter").default;
-var S3Adapter = require("parse-server-s3-adapter").default;
-var ParsePushAdapter = require("parse-server-push-adapter").default;
+describe('AdapterLoader', () => {
+ it('should instantiate an adapter from string in object', done => {
+ const adapterPath = require('path').resolve('./spec/support/MockAdapter');
-describe("AdapterLoader", ()=>{
-
- it("should instantiate an adapter from string in object", (done) => {
- var adapterPath = require('path').resolve("./spec/MockAdapter");
-
- var adapter = loadAdapter({
+ const adapter = loadAdapter({
adapter: adapterPath,
options: {
- key: "value",
- foo: "bar"
- }
+ key: 'value',
+ foo: 'bar',
+ },
});
- expect(adapter instanceof Object).toBe(true);
- expect(adapter.options.key).toBe("value");
- expect(adapter.options.foo).toBe("bar");
+ expect(Utils.isObject(adapter)).toBe(true);
+ expect(adapter.options.key).toBe('value');
+ expect(adapter.options.foo).toBe('bar');
done();
});
- it("should instantiate an adapter from string", (done) => {
- var adapterPath = require('path').resolve("./spec/MockAdapter");
- var adapter = loadAdapter(adapterPath);
+ it('should instantiate an adapter from string', done => {
+ const adapterPath = require('path').resolve('./spec/support/MockAdapter');
+ const adapter = loadAdapter(adapterPath);
- expect(adapter instanceof Object).toBe(true);
+ expect(Utils.isObject(adapter)).toBe(true);
done();
});
- it("should instantiate an adapter from string that is module", (done) => {
- var adapterPath = require('path').resolve("./src/Adapters/Files/FilesAdapter");
- var adapter = loadAdapter({
- adapter: adapterPath
+ it('should instantiate an adapter from string that is module', done => {
+ const adapterPath = require('path').resolve('./lib/Adapters/Files/FilesAdapter');
+ const adapter = loadAdapter({
+ adapter: adapterPath,
});
expect(typeof adapter).toBe('object');
@@ -45,72 +44,141 @@ describe("AdapterLoader", ()=>{
done();
});
- it("should instantiate an adapter from function/Class", (done) => {
- var adapter = loadAdapter({
- adapter: FilesAdapter
+ it('should instantiate an adapter from npm module', done => {
+ const adapter = loadAdapter({
+ module: '@parse/fs-files-adapter',
+ });
+
+ expect(typeof adapter).toBe('object');
+ expect(typeof adapter.createFile).toBe('function');
+ expect(typeof adapter.deleteFile).toBe('function');
+ expect(typeof adapter.getFileData).toBe('function');
+ expect(typeof adapter.getFileLocation).toBe('function');
+ done();
+ });
+
+ it('should instantiate an adapter from function/Class', done => {
+ const adapter = loadAdapter({
+ adapter: FilesAdapter,
});
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
- it("should instantiate the default adapter from Class", (done) => {
- var adapter = loadAdapter(null, FilesAdapter);
+ it('should instantiate the default adapter from Class', done => {
+ const adapter = loadAdapter(null, FilesAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
- it("should use the default adapter", (done) => {
- var defaultAdapter = new FilesAdapter();
- var adapter = loadAdapter(null, defaultAdapter);
+ it('should use the default adapter', done => {
+ const defaultAdapter = new FilesAdapter();
+ const adapter = loadAdapter(null, defaultAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
- it("should use the provided adapter", (done) => {
- var originalAdapter = new FilesAdapter();
- var adapter = loadAdapter(originalAdapter);
+ it('should use the provided adapter', done => {
+ const originalAdapter = new FilesAdapter();
+ const adapter = loadAdapter(originalAdapter);
expect(adapter).toBe(originalAdapter);
done();
});
- it("should fail loading an improperly configured adapter", (done) => {
- var Adapter = function(options) {
+ it('should fail loading an improperly configured adapter', done => {
+ const Adapter = function (options) {
if (!options.foo) {
- throw "foo is required for that adapter";
+ throw 'foo is required for that adapter';
}
- }
- var adapterOptions = {
- param: "key",
- doSomething: function() {}
+ };
+ const adapterOptions = {
+ param: 'key',
+ doSomething: function () {},
};
expect(() => {
- var adapter = loadAdapter(adapterOptions, Adapter);
+ const adapter = loadAdapter(adapterOptions, Adapter);
expect(adapter).toEqual(adapterOptions);
- }).not.toThrow("foo is required for that adapter");
+ }).not.toThrow('foo is required for that adapter');
done();
});
- it("should load push adapter from options", (done) => {
- var options = {
- ios: {
- bundleId: 'bundle.id'
- }
- }
+ it('should load push adapter from options', async () => {
+ const options = {
+ android: {
+ firebaseServiceAccount: {
+ "type": "service_account",
+ "project_id": "example-xxxx",
+ "private_key_id": "xxxx",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxFcVMD9L2xJWW\nEMi4w/XIBPvX5bTStIEdt4GY+yfrmCHspaVdgpTcHlTLA60sAGTFdorPprOwAm6f\njaTG4j86zfW25GF6AlFO/8vE2B0tjreuQQtcP9gkWJmsTp8yzXDirDQ43Kv93Kbc\nUPmsyAN5WB8XiFjjWLnFCeDiOVdd8sHfG0HYldNzyYwXrOTLE5kOjASYSJDzdrfI\nwN9PzZC7+cCy/DDzTRKQCqfz9pEZmxqJk4Id5HLVNkGKgji3C3b6o3MXWPS+1+zD\nGheKC9WLDZnCVycAnNHFiPpsp7R82lLKC3Dth37b6qzJO+HwfTmzCb0/xCVJ0/mZ\nC4Mxih/bAgMBAAECggEACbL1DvDw75Yd0U3TCJenDxEC0DTjHgVH6x5BaWUcLyGy\nffkmoQQFbjb1Evd9FSNiYZRYDv6E6feAIpoJ8+CxcOGV+zHwCtQ0qtyExx/FHVkr\nQ06JtkBC8N6vcAoQWyJ4c9nVtGWVv/5FX1zKCAYedpd2gH31zGHwLtQXLpzQZbNO\nO/0rcggg4unGSUIyw5437XiyckJ3QdneSEPe9HvY2wxLn/f1PjMpRYiNLBSuaFBJ\n+MYXr//Vh7cMInQk5/pMFbGxugNb7dtjgvm3LKRssKnubEOyrKldo8DVJmAvjhP4\nWboOOBVEo2ZhXgnBjeMvI8btXlJ85h9lZ7xwqfWsjQKBgQDkrrLpA3Mm21rsP1Ar\nMLEnYTdMZ7k+FTm5pJffPOsC7wiLWdRLwwrtb0V3kC3jr2K4SZY/OEV8IAWHfut/\n8mP8cPQPJiFp92iOgde4Xq/Ycwx4ZAXUj7mHHgywFi2K0xATzgc9sgX3NCVl9utR\nIU/FbEDCLxyD4T3Jb5gL3xFdhwKBgQDGPS46AiHuYmV7OG4gEOsNdczTppBJCgTt\nKGSJOxZg8sQodNJeWTPP2iQr4yJ4EY57NQmH7WSogLrGj8tmorEaL7I2kYlHJzGm\nniwApWEZlFc00xgXwV5d8ATfmAf8W1ZSZ6THbHesDUGjXSoL95k3KKXhnztjUT6I\n8d5qkCygDQKBgFN7p1rDZKVZzO6UCntJ8lJS/jIJZ6nPa9xmxv67KXxPsQnWSFdE\nI9gcF/sXCnmlTF/ElXIM4+j1c69MWULDRVciESb6n5YkuOnVYuAuyPk2vuWwdiRs\nN6mpAa7C2etlM+hW/XO7aswdIE4B/1QF2i5TX6zEMB/A+aJw98vVqmw/AoGADOm9\nUiADb9DPBXjGi6YueYD756mI6okRixU/f0TvDz+hEXWSonyzCE4QXx97hlC2dEYf\nKdCH5wYDpJ2HRVdBrBABTtaqF41xCYZyHVSof48PIyzA/AMnj3zsBFiV5JVaiSGh\nNTBWl0mBxg9yhrcJLvOh4pGJv81yAl+m+lAL6B0CgYEArtqtQ1YVLIUn4Pb/HDn8\nN8o7WbhloWQnG34iSsAG8yNtzbbxdugFrEm5ejPSgZ+dbzSzi/hizOFS/+/fwEdl\nay9jqY1fngoqSrS8eddUsY1/WAcmd6wPWEamsSjazA4uxQERruuFOi94E4b895KA\nqYe0A3xb0JL2ieAOZsn8XNA=\n-----END PRIVATE KEY-----\n",
+ "client_email": "test@example.com",
+ "client_id": "1",
+ "auth_uri": "https://example.com",
+ "token_uri": "https://example.com",
+ "auth_provider_x509_cert_url": "https://example.com",
+ "client_x509_cert_url": "https://example.com",
+ "universe_domain": "example.com"
+ }
+ },
+ };
+ const ParsePushAdapter = await loadModule('@parse/push-adapter');
expect(() => {
- var adapter = loadAdapter(undefined, ParsePushAdapter, options);
+ const adapter = loadAdapter(undefined, ParsePushAdapter, options);
expect(adapter.constructor).toBe(ParsePushAdapter);
expect(adapter).not.toBe(undefined);
}).not.toThrow();
+ });
+
+ it('should load custom push adapter from string (#3544)', done => {
+ const adapterPath = require('path').resolve('./spec/support/MockPushAdapter');
+ const options = {
+ ios: {
+ bundleId: 'bundle.id',
+ },
+ };
+ const pushAdapterOptions = {
+ adapter: adapterPath,
+ options,
+ };
+ expect(() => {
+ reconfigureServer({
+ push: pushAdapterOptions,
+ }).then(() => {
+ const config = Config.get(Parse.applicationId);
+ const pushAdapter = config.pushWorker.adapter;
+ expect(pushAdapter.getValidPushTypes()).toEqual(['ios']);
+ expect(pushAdapter.options).toEqual(pushAdapterOptions);
+ done();
+ });
+ }).not.toThrow();
+ });
+
+ it('should load custom database adapter from config', done => {
+ const adapterPath = require('path').resolve('./spec/support/MockDatabaseAdapter');
+ const options = {
+ databaseURI: 'oracledb://user:password@localhost:1521/freepdb1',
+ collectionPrefix: '',
+ };
+ const databaseAdapterOptions = {
+ adapter: adapterPath,
+ options,
+ };
+ expect(() => {
+ const databaseAdapter = loadAdapter(databaseAdapterOptions);
+ expect(databaseAdapter).not.toBe(undefined);
+ expect(databaseAdapter.options).toEqual(options);
+ expect(databaseAdapter.getDatabaseURI()).toEqual(options.databaseURI);
+ }).not.toThrow();
done();
});
- it("should load S3Adapter from direct passing", (done) => {
- var s3Adapter = new S3Adapter("key", "secret", "bucket")
+ it('should load file adapter from direct passing', done => {
+ spyOn(console, 'warn').and.callFake(() => {});
+ const mockFilesAdapter = new MockFilesAdapter('key', 'secret', 'bucket');
expect(() => {
- var adapter = loadAdapter(s3Adapter, FilesAdapter);
- expect(adapter).toBe(s3Adapter);
+ const adapter = loadAdapter(mockFilesAdapter, FilesAdapter);
+ expect(adapter).toBe(mockFilesAdapter);
}).not.toThrow();
done();
- })
+ });
});
diff --git a/spec/Adapters/Auth/BaseCodeAdapter.spec.js b/spec/Adapters/Auth/BaseCodeAdapter.spec.js
new file mode 100644
index 0000000000..fef4b43306
--- /dev/null
+++ b/spec/Adapters/Auth/BaseCodeAdapter.spec.js
@@ -0,0 +1,182 @@
+const BaseAuthCodeAdapter = require('../../../lib/Adapters/Auth/BaseCodeAuthAdapter').default;
+
+describe('BaseAuthCodeAdapter', function () {
+ let adapter;
+ const adapterName = 'TestAdapter';
+ const validOptions = {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ };
+
+ class TestAuthCodeAdapter extends BaseAuthCodeAdapter {
+ async getUserFromAccessToken(accessToken) {
+ if (accessToken === 'validAccessToken') {
+ return { id: 'validUserId' };
+ }
+ throw new Error('Invalid access token');
+ }
+
+ async getAccessTokenFromCode(authData) {
+ if (authData.code === 'validCode') {
+ return 'validAccessToken';
+ }
+ throw new Error('Invalid code');
+ }
+ }
+
+ beforeEach(function () {
+ adapter = new TestAuthCodeAdapter(adapterName);
+ });
+
+ describe('validateOptions', function () {
+ it('should throw error if options are missing', function () {
+ expect(() => adapter.validateOptions(null)).toThrowError(`${adapterName} options are required.`);
+ });
+
+ it('should throw error if clientId is missing in secure mode', function () {
+ expect(() =>
+ adapter.validateOptions({ clientSecret: 'validClientSecret' })
+ ).toThrowError(`${adapterName} clientId is required.`);
+ });
+
+ it('should throw error if clientSecret is missing in secure mode', function () {
+ expect(() =>
+ adapter.validateOptions({ clientId: 'validClientId' })
+ ).toThrowError(`${adapterName} clientSecret is required.`);
+ });
+
+ it('should not throw error for valid options', function () {
+ expect(() => adapter.validateOptions(validOptions)).not.toThrow();
+ expect(adapter.clientId).toBe('validClientId');
+ expect(adapter.clientSecret).toBe('validClientSecret');
+ expect(adapter.enableInsecureAuth).toBeUndefined();
+ });
+
+ it('should allow insecure mode without clientId or clientSecret', function () {
+ const options = { enableInsecureAuth: true };
+ expect(() => adapter.validateOptions(options)).not.toThrow();
+ expect(adapter.enableInsecureAuth).toBe(true);
+ });
+ });
+
+ describe('beforeFind', function () {
+ it('should throw error if code is missing in secure mode', async function () {
+ adapter.validateOptions(validOptions);
+ const authData = { access_token: 'validAccessToken' };
+
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWithError(
+ `${adapterName} code is required.`
+ );
+ });
+
+ it('should throw error if access token is missing in insecure mode', async function () {
+ adapter.validateOptions({ enableInsecureAuth: true });
+ const authData = {};
+
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWithError(
+ `${adapterName} auth is invalid for this user.`
+ );
+ });
+
+ it('should throw error if user ID does not match in insecure mode', async function () {
+ adapter.validateOptions({ enableInsecureAuth: true });
+ const authData = { id: 'invalidUserId', access_token: 'validAccessToken' };
+
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWithError(
+ `${adapterName} auth is invalid for this user.`
+ );
+ });
+
+ it('should process valid secure payload and update authData', async function () {
+ adapter.validateOptions(validOptions);
+ const authData = { code: 'validCode' };
+
+ await adapter.beforeFind(authData);
+
+ expect(authData.access_token).toBe('validAccessToken');
+ expect(authData.id).toBe('validUserId');
+ expect(authData.code).toBeUndefined();
+ });
+
+ it('should process valid insecure payload', async function () {
+ adapter.validateOptions({ enableInsecureAuth: true });
+ const authData = { id: 'validUserId', access_token: 'validAccessToken' };
+
+ await expectAsync(adapter.beforeFind(authData)).toBeResolved();
+ });
+ });
+
+ describe('getUserFromAccessToken', function () {
+ it('should throw error if not implemented in base class', async function () {
+ const baseAdapter = new BaseAuthCodeAdapter(adapterName);
+
+ await expectAsync(baseAdapter.getUserFromAccessToken('test')).toBeRejectedWithError(
+ 'getUserFromAccessToken is not implemented'
+ );
+ });
+
+ it('should return valid user for valid access token', async function () {
+ const user = await adapter.getUserFromAccessToken('validAccessToken', {});
+ expect(user).toEqual({ id: 'validUserId' });
+ });
+
+ it('should throw error for invalid access token', async function () {
+ await expectAsync(adapter.getUserFromAccessToken('invalidAccessToken', {})).toBeRejectedWithError(
+ 'Invalid access token'
+ );
+ });
+ });
+
+ describe('getAccessTokenFromCode', function () {
+ it('should throw error if not implemented in base class', async function () {
+ const baseAdapter = new BaseAuthCodeAdapter(adapterName);
+
+ await expectAsync(baseAdapter.getAccessTokenFromCode({ code: 'test' })).toBeRejectedWithError(
+ 'getAccessTokenFromCode is not implemented'
+ );
+ });
+
+ it('should return valid access token for valid code', async function () {
+ const accessToken = await adapter.getAccessTokenFromCode({ code: 'validCode' });
+ expect(accessToken).toBe('validAccessToken');
+ });
+
+ it('should throw error for invalid code', async function () {
+ await expectAsync(adapter.getAccessTokenFromCode({ code: 'invalidCode' })).toBeRejectedWithError(
+ 'Invalid code'
+ );
+ });
+ });
+
+ describe('validateLogin', function () {
+ it('should return user id from authData', function () {
+ const authData = { id: 'validUserId' };
+ const result = adapter.validateLogin(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+ });
+
+ describe('validateSetUp', function () {
+ it('should return user id from authData', function () {
+ const authData = { id: 'validUserId' };
+ const result = adapter.validateSetUp(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+ });
+
+ describe('afterFind', function () {
+ it('should return user id from authData', function () {
+ const authData = { id: 'validUserId' };
+ const result = adapter.afterFind(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+ });
+
+ describe('validateUpdate', function () {
+ it('should return user id from authData', function () {
+ const authData = { id: 'validUserId' };
+ const result = adapter.validateUpdate(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/gcenter.spec.js b/spec/Adapters/Auth/gcenter.spec.js
new file mode 100644
index 0000000000..45e94a527f
--- /dev/null
+++ b/spec/Adapters/Auth/gcenter.spec.js
@@ -0,0 +1,220 @@
+const GameCenterAuth = require('../../../lib/Adapters/Auth/gcenter').default;
+const { pki } = require('node-forge');
+const fs = require('fs');
+const path = require('path');
+
+describe('GameCenterAuth Adapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new GameCenterAuth.constructor();
+
+ const gcProd4 = fs.readFileSync(path.resolve(__dirname, '../../support/cert/gc-prod-4.cer'));
+ const digicertPem = fs.readFileSync(path.resolve(__dirname, '../../support/cert/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem')).toString();
+
+ mockFetch([
+ {
+ url: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ method: 'GET',
+ response: {
+ ok: true,
+ headers: new Map(),
+ arrayBuffer: () => Promise.resolve(
+ gcProd4.buffer.slice(gcProd4.byteOffset, gcProd4.byteOffset + gcProd4.length)
+ ),
+ },
+ },
+ {
+ url: 'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
+ method: 'GET',
+ response: {
+ ok: true,
+ headers: new Map([['content-type', 'application/x-pem-file'], ['content-length', digicertPem.length.toString()]]),
+ text: () => Promise.resolve(digicertPem),
+ },
+ }
+ ]);
+ });
+
+ describe('Test config failing due to missing params or wrong types', function () {
+ it('should throw error for invalid options', async function () {
+ const invalidOptions = [
+ null,
+ undefined,
+ {},
+ { bundleId: '' },
+ { enableInsecureAuth: false }, // Missing bundleId in secure mode
+ ];
+
+ for (const options of invalidOptions) {
+ expect(() => adapter.validateOptions(options)).withContext(JSON.stringify(options)).toThrow()
+ }
+ });
+
+ it('should validate options successfully with valid parameters', function () {
+ const validOptions = { bundleId: 'com.valid.app', enableInsecureAuth: false };
+ expect(() => adapter.validateOptions(validOptions)).not.toThrow();
+ });
+ });
+
+ describe('Test payload failing due to missing params or wrong types', function () {
+ it('should throw error for missing authData fields', async function () {
+ await expectAsync(adapter.validateAuthData({})).toBeRejectedWithError(
+ 'AuthData id is missing.'
+ );
+ });
+ });
+
+ describe('Test payload fails due to incorrect appId / certificate', function () {
+ it('should throw error for invalid publicKeyUrl', async function () {
+ const invalidPublicKeyUrl = 'https://malicious.url.com/key.cer';
+
+ spyOn(adapter, 'fetchCertificate').and.throwError(
+ new Error('Invalid publicKeyUrl')
+ );
+
+ await expectAsync(
+ adapter.getAppleCertificate(invalidPublicKeyUrl)
+ ).toBeRejectedWithError('Invalid publicKeyUrl: https://malicious.url.com/key.cer');
+ });
+
+ it('should throw error for invalid signature verification', async function () {
+ const fakePublicKey = 'invalid-key';
+ const fakeAuthData = {
+ id: '1234567',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1460981421303,
+ salt: 'saltST==',
+ signature: 'invalidSignature',
+ };
+
+ spyOn(adapter, 'getAppleCertificate').and.returnValue(Promise.resolve(fakePublicKey));
+ spyOn(adapter, 'verifySignature').and.throwError('Invalid signature.');
+
+ await expectAsync(adapter.validateAuthData(fakeAuthData)).toBeRejectedWithError(
+ 'Invalid signature.'
+ );
+ });
+ });
+
+ describe('Test payload passing', function () {
+ it('should successfully process valid payload and save auth data', async function () {
+ const validAuthData = {
+ id: '1234567',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1460981421303,
+ salt: 'saltST==',
+ signature: 'validSignature',
+ bundleId: 'com.valid.app',
+ };
+
+ spyOn(adapter, 'getAppleCertificate').and.returnValue(Promise.resolve('validKey'));
+ spyOn(adapter, 'verifySignature').and.returnValue(true);
+
+ await expectAsync(adapter.validateAuthData(validAuthData)).toBeResolved();
+ });
+ });
+
+ describe('Certificate and Signature Validation', function () {
+ it('should fetch and validate Apple certificate', async function () {
+ const certUrl = 'https://static.gc.apple.com/public-key/gc-prod-4.cer';
+ const mockCertificate = 'mockCertificate';
+
+ spyOn(adapter, 'fetchCertificate').and.returnValue(
+ Promise.resolve({ certificate: mockCertificate, headers: new Map() })
+ );
+ spyOn(pki, 'certificateFromPem').and.returnValue({});
+
+ adapter.cache[certUrl] = mockCertificate;
+
+ const cert = await adapter.getAppleCertificate(certUrl);
+ expect(cert).toBe(mockCertificate);
+ });
+
+ it('should verify signature successfully', async function () {
+ const authData = {
+ id: 'G:1965586982',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1565257031287,
+ signature:
+ 'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
+ salt: 'DzqqrQ==',
+ };
+
+ adapter.bundleId = 'cloud.xtralife.gamecenterauth';
+ adapter.enableInsecureAuth = false;
+
+ spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();
+
+ const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);
+
+ expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();
+
+ });
+
+ it('should not use bundle id from authData payload in secure mode', async function () {
+ const authData = {
+ id: 'G:1965586982',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1565257031287,
+ signature:
+ 'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
+ salt: 'DzqqrQ==',
+ bundleId: 'com.example.insecure.app',
+ };
+
+ adapter.bundleId = 'cloud.xtralife.gamecenterauth';
+ adapter.enableInsecureAuth = false;
+
+ spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();
+
+ const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);
+
+ expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();
+
+ });
+
+ it('should not use bundle id from authData payload in insecure mode', async function () {
+ const authData = {
+ id: 'G:1965586982',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1565257031287,
+ signature:
+ 'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
+ salt: 'DzqqrQ==',
+ bundleId: 'com.example.insecure.app',
+ };
+
+ adapter.bundleId = 'cloud.xtralife.gamecenterauth';
+ adapter.enableInsecureAuth = true;
+
+ spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();
+
+ const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);
+
+ expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();
+
+ });
+
+ it('can use bundle id from authData payload in insecure mode', async function () {
+ const authData = {
+ id: 'G:1965586982',
+ publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
+ timestamp: 1565257031287,
+ signature:
+ 'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
+ salt: 'DzqqrQ==',
+ bundleId: 'cloud.xtralife.gamecenterauth',
+ };
+
+ adapter.enableInsecureAuth = true;
+
+ spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();
+
+ const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);
+
+ expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();
+
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/github.spec.js b/spec/Adapters/Auth/github.spec.js
new file mode 100644
index 0000000000..c12d002ed9
--- /dev/null
+++ b/spec/Adapters/Auth/github.spec.js
@@ -0,0 +1,285 @@
+const GitHubAdapter = require('../../../lib/Adapters/Auth/github').default;
+
+describe('GitHubAdapter', function () {
+ let adapter;
+ const validOptions = {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ };
+
+ beforeEach(function () {
+ adapter = new GitHubAdapter.constructor();
+ adapter.validateOptions(validOptions);
+ });
+
+ describe('getAccessTokenFromCode', function () {
+ it('should fetch an access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken',
+ }),
+ },
+ },
+ ]);
+
+ const code = 'validCode';
+ const token = await adapter.getAccessTokenFromCode(code);
+
+ expect(token).toBe('mockAccessToken');
+ });
+
+ it('should throw an error if the response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ const code = 'invalidCode';
+
+ await expectAsync(adapter.getAccessTokenFromCode(code)).toBeRejectedWithError(
+ 'Failed to exchange code for token: Bad Request'
+ );
+ });
+
+ it('should throw an error if the response contains an error', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ error: 'invalid_grant',
+ error_description: 'Code is invalid',
+ }),
+ },
+ },
+ ]);
+
+ const code = 'invalidCode';
+
+ await expectAsync(adapter.getAccessTokenFromCode(code)).toBeRejectedWithError('Code is invalid');
+ });
+ });
+
+ describe('getUserFromAccessToken', function () {
+ it('should fetch user data successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ login: 'mockUserLogin',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+ const user = await adapter.getUserFromAccessToken(accessToken);
+
+ expect(user).toEqual({ id: 'mockUserId', login: 'mockUserLogin' });
+ });
+
+ it('should throw an error if the response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const accessToken = 'invalidAccessToken';
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken)).toBeRejectedWithError(
+ 'Failed to fetch GitHub user: Unauthorized'
+ );
+ });
+
+ it('should throw an error if user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken)).toBeRejectedWithError(
+ 'Invalid GitHub user data received.'
+ );
+ });
+ });
+
+ describe('GitHubAdapter E2E Test', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ github: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ },
+ },
+ });
+ });
+
+ it('should log in user using GitHub adapter successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ login: 'mockUserLogin',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode' };
+ const user = await Parse.User.logInWith('github', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle error when GitHub returns invalid code', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Invalid code',
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode' };
+
+ await expectAsync(Parse.User.logInWith('github', { authData })).toBeRejectedWithError(
+ 'Failed to exchange code for token: Invalid code'
+ );
+ });
+
+ it('should handle error when GitHub returns invalid user data', async function () {
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode' };
+
+ await expectAsync(Parse.User.logInWith('github', { authData })).toBeRejectedWithError(
+ 'Failed to fetch GitHub user: Unauthorized'
+ );
+ });
+
+ it('e2e secure does not support insecure payload', async function () {
+ mockFetch();
+ const authData = { id: 'mockUserId', access_token: 'mockAccessToken123' };
+ await expectAsync(Parse.User.logInWith('github', { authData })).toBeRejectedWithError(
+ 'GitHub code is required.'
+ );
+ });
+
+ it('e2e insecure does support secure payload', async function () {
+ await reconfigureServer({
+ auth: {
+ github: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ mockFetch([
+ {
+ url: 'https://github.com/login/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.github.com/user',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ login: 'mockUserLogin',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode' };
+ const user = await Parse.User.logInWith('github', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/gpgames.spec.js b/spec/Adapters/Auth/gpgames.spec.js
new file mode 100644
index 0000000000..8f3a71e46c
--- /dev/null
+++ b/spec/Adapters/Auth/gpgames.spec.js
@@ -0,0 +1,356 @@
+const GooglePlayGamesServicesAdapter = require('../../../lib/Adapters/Auth/gpgames').default;
+
+describe('GooglePlayGamesServicesAdapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new GooglePlayGamesServicesAdapter.constructor();
+ adapter.clientId = 'validClientId';
+ adapter.clientSecret = 'validClientSecret';
+ });
+
+ describe('getAccessTokenFromCode', function () {
+ it('should fetch an access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken',
+ }),
+ },
+ },
+ ]);
+
+ const code = 'validCode';
+ const authData = { redirectUri: 'http://example.com' };
+ const token = await adapter.getAccessTokenFromCode(code, authData);
+
+ expect(token).toBe('mockAccessToken');
+ });
+
+ it('should throw an error if the response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ const code = 'invalidCode';
+ const authData = { redirectUri: 'http://example.com' };
+
+ await expectAsync(adapter.getAccessTokenFromCode(code, authData)).toBeRejectedWithError(
+ 'Failed to exchange code for token: Bad Request'
+ );
+ });
+
+ it('should throw an error if the response contains an error', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ error: 'invalid_grant',
+ error_description: 'Code is invalid',
+ }),
+ },
+ },
+ ]);
+
+ const code = 'invalidCode';
+ const authData = { redirectUri: 'http://example.com' };
+
+ await expectAsync(adapter.getAccessTokenFromCode(code, authData)).toBeRejectedWithError(
+ 'Code is invalid'
+ );
+ });
+ });
+
+ describe('getUserFromAccessToken', function () {
+ it('should fetch user data successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ playerId: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+ const authData = { id: 'mockUserId' };
+ const user = await adapter.getUserFromAccessToken(accessToken, authData);
+
+ expect(user).toEqual({ id: 'mockUserId' });
+ });
+
+ it('should throw an error if the response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const accessToken = 'invalidAccessToken';
+ const authData = { id: 'mockUserId' };
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken, authData)).toBeRejectedWithError(
+ 'Failed to fetch Google Play Games Services user: Unauthorized'
+ );
+ });
+
+ it('should throw an error if user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+ const authData = { id: 'mockUserId' };
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken, authData)).toBeRejectedWithError(
+ 'Invalid Google Play Games Services user data received.'
+ );
+ });
+
+ it('should throw an error if playerId does not match the provided user ID', async function () {
+ mockFetch([
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ playerId: 'anotherUserId',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+ const authData = { id: 'mockUserId' };
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken, authData)).toBeRejectedWithError(
+ 'Invalid Google Play Games Services user data received.'
+ );
+ });
+ });
+
+ describe('GooglePlayGamesServicesAdapter E2E Test', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ gpgames: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ },
+ },
+ });
+ });
+
+ it('should log in user successfully with valid code', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ playerId: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ redirectUri: 'http://example.com',
+ };
+
+ const user = await Parse.User.logInWith('gpgames', { authData });
+
+ expect(user.id).toBeDefined();
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://oauth2.googleapis.com/token',
+ jasmine.any(Object)
+ );
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://www.googleapis.com/games/v1/players/mockUserId',
+ jasmine.any(Object)
+ );
+ });
+
+ it('should handle error when the token exchange fails', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Invalid code',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ redirectUri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('gpgames', { authData })).toBeRejectedWithError(
+ 'Failed to exchange code for token: Invalid code'
+ );
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://oauth2.googleapis.com/token',
+ jasmine.any(Object)
+ );
+ });
+
+ it('should handle error when user data fetch fails', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ redirectUri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('gpgames', { authData })).toBeRejectedWithError(
+ 'Failed to fetch Google Play Games Services user: Unauthorized'
+ );
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://oauth2.googleapis.com/token',
+ jasmine.any(Object)
+ );
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://www.googleapis.com/games/v1/players/mockUserId',
+ jasmine.any(Object)
+ );
+ });
+
+ it('should handle error when user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://www.googleapis.com/games/v1/players/mockUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ playerId: 'anotherUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ redirectUri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('gpgames', { authData })).toBeRejectedWithError(
+ 'Invalid Google Play Games Services user data received.'
+ );
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://oauth2.googleapis.com/token',
+ jasmine.any(Object)
+ );
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://www.googleapis.com/games/v1/players/mockUserId',
+ jasmine.any(Object)
+ );
+ });
+
+ it('should handle error when no code or access token is provided', async function () {
+ mockFetch();
+
+ const authData = {
+ id: 'mockUserId',
+ };
+
+ await expectAsync(Parse.User.logInWith('gpgames', { authData })).toBeRejectedWithError(
+ 'gpgames code is required.'
+ );
+
+ expect(global.fetch).not.toHaveBeenCalled();
+ });
+ });
+
+});
+
diff --git a/spec/Adapters/Auth/instagram.spec.js b/spec/Adapters/Auth/instagram.spec.js
new file mode 100644
index 0000000000..9b9fd27aa4
--- /dev/null
+++ b/spec/Adapters/Auth/instagram.spec.js
@@ -0,0 +1,283 @@
+const InstagramAdapter = require('../../../lib/Adapters/Auth/instagram').default;
+
+describe('InstagramAdapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new InstagramAdapter.constructor();
+ adapter.clientId = 'validClientId';
+ adapter.clientSecret = 'validClientSecret';
+ adapter.redirectUri = 'https://example.com/callback';
+ });
+
+ describe('getAccessTokenFromCode', function () {
+ it('should fetch an access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode' };
+ const token = await adapter.getAccessTokenFromCode(authData);
+
+ expect(token).toBe('mockAccessToken');
+ });
+
+ it('should throw an error if the response contains an error', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ error: 'invalid_grant',
+ error_description: 'Code is invalid',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode' };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Code is invalid'
+ );
+ });
+ });
+
+ describe('getUserFromAccessToken', function () {
+ it('should fetch user data successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'mockAccessToken';
+ const authData = { id: 'mockUserId' };
+ const user = await adapter.getUserFromAccessToken(accessToken, authData);
+
+ expect(user).toEqual({ id: 'mockUserId' });
+ });
+
+ it('should throw an error if user ID does not match authData', async function () {
+ mockFetch([
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'differentUserId',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'mockAccessToken';
+ const authData = { id: 'mockUserId' };
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken, authData)).toBeRejectedWithError(
+ 'Instagram auth is invalid for this user.'
+ );
+ });
+
+ it('should ignore client-provided apiURL and use hardcoded endpoint', async () => {
+ const accessToken = 'mockAccessToken';
+ const authData = {
+ id: 'mockUserId',
+ apiURL: 'https://example.com/',
+ };
+
+ mockFetch([
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const user = await adapter.getUserFromAccessToken(accessToken, authData);
+ expect(user).toEqual({ id: 'mockUserId' });
+ });
+ });
+
+ describe('InstagramAdapter E2E Test', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ instagram: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ redirectUri: 'https://example.com/callback',
+ },
+ },
+ });
+ });
+
+ it('should log in user successfully with valid code', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken123',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ };
+
+ const user = await Parse.User.logInWith('instagram', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle error when access token exchange fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Invalid code',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ };
+
+ await expectAsync(Parse.User.logInWith('instagram', { authData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Instagram API request failed.')
+ );
+ });
+
+ it('should handle error when user data fetch fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken123',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ };
+
+ await expectAsync(Parse.User.logInWith('instagram', { authData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Instagram API request failed.')
+ );
+ });
+
+ it('should handle error when user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://graph.instagram.com/me?fields=id&access_token=mockAccessToken123',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'differentUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ id: 'mockUserId',
+ };
+
+ await expectAsync(Parse.User.logInWith('instagram', { authData })).toBeRejectedWithError(
+ 'Instagram auth is invalid for this user.'
+ );
+ });
+
+ it('should handle error when no code or access token is provided', async function () {
+ mockFetch();
+
+ const authData = {
+ id: 'mockUserId',
+ };
+
+ await expectAsync(Parse.User.logInWith('instagram', { authData })).toBeRejectedWithError(
+ 'Instagram code is required.'
+ );
+ });
+ });
+
+});
diff --git a/spec/Adapters/Auth/line.spec.js b/spec/Adapters/Auth/line.spec.js
new file mode 100644
index 0000000000..bde4c906b8
--- /dev/null
+++ b/spec/Adapters/Auth/line.spec.js
@@ -0,0 +1,309 @@
+const LineAdapter = require('../../../lib/Adapters/Auth/line').default;
+describe('LineAdapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new LineAdapter.constructor();
+ adapter.clientId = 'validClientId';
+ adapter.clientSecret = 'validClientSecret';
+ });
+
+ describe('getAccessTokenFromCode', function () {
+ it('should throw an error if code is missing in authData', async function () {
+ const authData = { redirect_uri: 'http://example.com' };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Line auth is invalid for this user.'
+ );
+ });
+
+ it('should fetch an access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ const token = await adapter.getAccessTokenFromCode(authData);
+
+ expect(token).toBe('mockAccessToken');
+ });
+
+ it('should throw an error if response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Failed to exchange code for token: Bad Request'
+ );
+ });
+
+ it('should throw an error if response contains an error object', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ error: 'invalid_grant',
+ error_description: 'Code is invalid',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Code is invalid'
+ );
+ });
+ });
+
+ describe('getUserFromAccessToken', function () {
+ it('should fetch user data successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ userId: 'mockUserId',
+ displayName: 'mockDisplayName',
+ }),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+ const user = await adapter.getUserFromAccessToken(accessToken);
+
+ expect(user).toEqual({
+ userId: 'mockUserId',
+ displayName: 'mockDisplayName',
+ });
+ });
+
+ it('should throw an error if response is not ok', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const accessToken = 'invalidAccessToken';
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken)).toBeRejectedWithError(
+ 'Failed to fetch Line user: Unauthorized'
+ );
+ });
+
+ it('should throw an error if user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const accessToken = 'validAccessToken';
+
+ await expectAsync(adapter.getUserFromAccessToken(accessToken)).toBeRejectedWithError(
+ 'Invalid Line user data received.'
+ );
+ });
+ });
+
+ describe('LineAdapter E2E Test', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ line: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ },
+ },
+ });
+ });
+
+ it('should log in user successfully with valid code', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ userId: 'mockUserId',
+ displayName: 'mockDisplayName',
+ }),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ const user = await Parse.User.logInWith('line', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle error when token exchange fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Invalid code',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('line', { authData })).toBeRejectedWithError(
+ 'Failed to exchange code for token: Invalid code'
+ );
+ });
+
+ it('should handle error when user data fetch fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('line', { authData })).toBeRejectedWithError(
+ 'Failed to fetch Line user: Unauthorized'
+ );
+ });
+
+ it('should handle error when user data is invalid', async function () {
+ mockFetch([
+ {
+ url: 'https://api.line.me/oauth2/v2.1/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.line.me/v2/profile',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('line', { authData })).toBeRejectedWithError(
+ 'Invalid Line user data received.'
+ );
+ });
+
+ it('should handle error when no code is provided', async function () {
+ mockFetch();
+
+ const authData = {
+ redirect_uri: 'http://example.com',
+ };
+
+ await expectAsync(Parse.User.logInWith('line', { authData })).toBeRejectedWithError(
+ 'Line code is required.'
+ );
+ });
+ });
+
+});
diff --git a/spec/Adapters/Auth/linkedIn.spec.js b/spec/Adapters/Auth/linkedIn.spec.js
new file mode 100644
index 0000000000..9f5a4b37ae
--- /dev/null
+++ b/spec/Adapters/Auth/linkedIn.spec.js
@@ -0,0 +1,333 @@
+
+const LinkedInAdapter = require('../../../lib/Adapters/Auth/linkedin').default;
+describe('LinkedInAdapter', function () {
+ let adapter;
+ const validOptions = {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: false,
+ };
+
+ beforeEach(function () {
+ adapter = new LinkedInAdapter.constructor();
+ });
+
+ describe('Test configuration errors', function () {
+ it('should throw error for missing options', function () {
+ const invalidOptions = [null, undefined, {}, { clientId: 'validClientId' }];
+
+ for (const options of invalidOptions) {
+ expect(() => {
+ adapter.validateOptions(options);
+ }).toThrow();
+ }
+ });
+
+ it('should validate options successfully with valid parameters', function () {
+ expect(() => {
+ adapter.validateOptions(validOptions);
+ }).not.toThrow();
+ expect(adapter.clientId).toBe(validOptions.clientId);
+ expect(adapter.clientSecret).toBe(validOptions.clientSecret);
+ expect(adapter.enableInsecureAuth).toBe(validOptions.enableInsecureAuth);
+ });
+ });
+
+ describe('Test beforeFind', function () {
+ it('should throw error for invalid payload', async function () {
+ adapter.enableInsecureAuth = true;
+
+ const payloads = [{}, { access_token: null }];
+
+ for (const payload of payloads) {
+ await expectAsync(adapter.beforeFind(payload)).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LinkedIn auth is invalid for this user.')
+ );
+ }
+ });
+
+ it('should process secure payload and set auth data', async function () {
+ spyOn(adapter, 'getAccessTokenFromCode').and.returnValue(
+ Promise.resolve('validToken')
+ );
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
+ Promise.resolve({ id: 'validUserId' })
+ );
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com', is_mobile_sdk: false };
+
+ await adapter.beforeFind(authData);
+
+ expect(authData.access_token).toBe('validToken');
+ expect(authData.id).toBe('validUserId');
+ });
+
+ it('should validate insecure auth and match user id', async function () {
+ adapter.enableInsecureAuth = true;
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
+ Promise.resolve({ id: 'validUserId' })
+ );
+
+ const authData = { access_token: 'validToken', id: 'validUserId', is_mobile_sdk: false };
+
+ await expectAsync(adapter.beforeFind(authData)).toBeResolved();
+ });
+
+ it('should throw error if insecure auth user id does not match', async function () {
+ adapter.enableInsecureAuth = true;
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
+ Promise.resolve({ id: 'invalidUserId' })
+ );
+
+ const authData = { access_token: 'validToken', id: 'validUserId', is_mobile_sdk: false };
+
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWith(
+ new Error('LinkedIn auth is invalid for this user.')
+ );
+ });
+ });
+
+ describe('Test getUserFromAccessToken', function () {
+ it('should fetch user successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.linkedin.com/v2/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ id: 'validUserId' }),
+ },
+ },
+ ]);
+
+ const user = await adapter.getUserFromAccessToken('validToken', false);
+
+ expect(global.fetch).toHaveBeenCalledWith('https://api.linkedin.com/v2/me', {
+ headers: {
+ Authorization: `Bearer validToken`,
+ 'x-li-format': 'json',
+ 'x-li-src': undefined,
+ },
+ method: 'GET',
+ });
+ expect(user).toEqual({ id: 'validUserId' });
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://api.linkedin.com/v2/me',
+ method: 'GET',
+ response: {
+ ok: false,
+ },
+ },
+ ]);
+
+ await expectAsync(adapter.getUserFromAccessToken('invalidToken', false)).toBeRejectedWith(
+ new Error('LinkedIn API request failed.')
+ );
+ });
+ });
+
+ describe('Test getAccessTokenFromCode', function () {
+ it('should fetch token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://www.linkedin.com/oauth/v2/accessToken',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validToken' }),
+ },
+ },
+ ]);
+
+ const tokenResponse = await adapter.getAccessTokenFromCode('validCode', 'http://example.com');
+
+ expect(global.fetch).toHaveBeenCalledWith('https://www.linkedin.com/oauth/v2/accessToken', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: jasmine.any(URLSearchParams),
+ });
+ expect(tokenResponse).toEqual('validToken');
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://www.linkedin.com/oauth/v2/accessToken',
+ method: 'POST',
+ response: {
+ ok: false,
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.getAccessTokenFromCode('invalidCode', 'http://example.com')
+ ).toBeRejectedWith(new Error('LinkedIn API request failed.'));
+ });
+ });
+
+ describe('Test validate methods', function () {
+ const authData = { id: 'validUserId', access_token: 'validToken' };
+
+ it('validateLogin should return user id', function () {
+ const result = adapter.validateLogin(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+
+ it('validateSetUp should return user id', function () {
+ const result = adapter.validateSetUp(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+
+ it('validateUpdate should return user id', function () {
+ const result = adapter.validateUpdate(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+
+ it('afterFind should return user id', function () {
+ const result = adapter.afterFind(authData);
+ expect(result).toEqual({ id: 'validUserId' });
+ });
+ });
+
+ describe('LinkedInAdapter E2E Test', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ linkedin: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ },
+ },
+ });
+ });
+
+ it('should log in user using LinkedIn adapter successfully (secure)', async function () {
+ mockFetch([
+ {
+ url: 'https://www.linkedin.com/oauth/v2/accessToken',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.linkedin.com/v2/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'https://example.com/callback' };
+ const user = await Parse.User.logInWith('linkedin', { authData });
+
+ expect(user.id).toBeDefined();
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://www.linkedin.com/oauth/v2/accessToken',
+ jasmine.any(Object)
+ );
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.linkedin.com/v2/me',
+ jasmine.any(Object)
+ );
+ });
+
+ it('should handle error when LinkedIn returns invalid user data', async function () {
+ mockFetch([
+ {
+ url: 'https://www.linkedin.com/oauth/v2/accessToken',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ access_token: 'mockAccessToken123',
+ }),
+ },
+ },
+ {
+ url: 'https://api.linkedin.com/v2/me',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'https://example.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('linkedin', { authData })).toBeRejectedWithError(
+ 'LinkedIn API request failed.'
+ );
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://www.linkedin.com/oauth/v2/accessToken',
+ jasmine.any(Object)
+ );
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.linkedin.com/v2/me',
+ jasmine.any(Object)
+ );
+ });
+
+ it('secure does not support insecure payload if not enabled', async function () {
+ mockFetch();
+ const authData = { id: 'mockUserId', access_token: 'mockAccessToken123' };
+ await expectAsync(Parse.User.logInWith('linkedin', { authData })).toBeRejectedWithError(
+ 'LinkedIn code is required.'
+ );
+
+ expect(global.fetch).not.toHaveBeenCalled();
+ });
+
+ it('insecure mode supports insecure payload if enabled', async function () {
+ await reconfigureServer({
+ auth: {
+ linkedin: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ mockFetch([
+ {
+ url: 'https://api.linkedin.com/v2/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ id: 'mockUserId',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { id: 'mockUserId', access_token: 'mockAccessToken123' };
+ const user = await Parse.User.logInWith('linkedin', { authData });
+
+ expect(user.id).toBeDefined();
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.linkedin.com/v2/me',
+ jasmine.any(Object)
+ );
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/microsoft.spec.js b/spec/Adapters/Auth/microsoft.spec.js
new file mode 100644
index 0000000000..c5cf58b807
--- /dev/null
+++ b/spec/Adapters/Auth/microsoft.spec.js
@@ -0,0 +1,307 @@
+const MicrosoftAdapter = require('../../../lib/Adapters/Auth/microsoft').default;
+
+describe('MicrosoftAdapter', function () {
+ let adapter;
+ const validOptions = {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: false,
+ };
+
+ beforeEach(function () {
+ adapter = new MicrosoftAdapter.constructor();
+ });
+
+ describe('Test configuration errors', function () {
+ it('should throw error for missing options', function () {
+ const invalidOptions = [null, undefined, {}, { clientId: 'validClientId' }];
+
+ for (const options of invalidOptions) {
+ expect(() => {
+ adapter.validateOptions(options);
+ }).toThrow();
+ }
+ });
+
+ it('should validate options successfully with valid parameters', function () {
+ expect(() => {
+ adapter.validateOptions(validOptions);
+ }).not.toThrow();
+ expect(adapter.clientId).toBe(validOptions.clientId);
+ expect(adapter.clientSecret).toBe(validOptions.clientSecret);
+ expect(adapter.enableInsecureAuth).toBe(validOptions.enableInsecureAuth);
+ });
+ });
+
+ describe('Test getUserFromAccessToken', function () {
+ it('should fetch user successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ id: 'validUserId' }),
+ },
+ },
+ ]);
+
+ const user = await adapter.getUserFromAccessToken('validToken');
+
+ expect(global.fetch).toHaveBeenCalledWith('https://graph.microsoft.com/v1.0/me', {
+ headers: {
+ Authorization: 'Bearer validToken',
+ },
+ method: 'GET',
+ });
+ expect(user).toEqual({ id: 'validUserId' });
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: { ok: false },
+ },
+ ]);
+
+ await expectAsync(adapter.getUserFromAccessToken('invalidToken')).toBeRejectedWith(
+ new Error('Microsoft API request failed.')
+ );
+ });
+ });
+
+ describe('Test getAccessTokenFromCode', function () {
+ it('should fetch token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validToken' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com' };
+ const token = await adapter.getAccessTokenFromCode(authData);
+
+ expect(global.fetch).toHaveBeenCalledWith('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: jasmine.any(URLSearchParams),
+ });
+ expect(token).toEqual('validToken');
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ method: 'POST',
+ response: { ok: false },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'http://example.com' };
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWith(
+ new Error('Microsoft API request failed.')
+ );
+ });
+ });
+
+ describe('Test secure authentication flow', function () {
+ it('should exchange code for access token and fetch user data', async function () {
+ spyOn(adapter, 'getAccessTokenFromCode').and.returnValue(Promise.resolve('validToken'));
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(Promise.resolve({ id: 'validUserId' }));
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com' };
+ await adapter.beforeFind(authData);
+
+ expect(authData.access_token).toBe('validToken');
+ expect(authData.id).toBe('validUserId');
+ });
+
+ it('should throw error if user data cannot be fetched', async function () {
+ spyOn(adapter, 'getAccessTokenFromCode').and.returnValue(Promise.resolve('validToken'));
+ spyOn(adapter, 'getUserFromAccessToken').and.throwError('Microsoft API request failed.');
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com' };
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWith(
+ new Error('Microsoft API request failed.')
+ );
+ });
+ });
+
+ describe('Test insecure authentication flow', function () {
+ beforeEach(function () {
+ adapter.enableInsecureAuth = true;
+ });
+
+ it('should validate insecure auth and match user id', async function () {
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
+ Promise.resolve({ id: 'validUserId' })
+ );
+
+ const authData = { access_token: 'validToken', id: 'validUserId' };
+ await expectAsync(adapter.beforeFind(authData)).toBeResolved();
+ });
+
+ it('should throw error if insecure auth user id does not match', async function () {
+ spyOn(adapter, 'getUserFromAccessToken').and.returnValue(
+ Promise.resolve({ id: 'invalidUserId' })
+ );
+
+ const authData = { access_token: 'validToken', id: 'validUserId' };
+ await expectAsync(adapter.beforeFind(authData)).toBeRejectedWith(
+ new Error('Microsoft auth is invalid for this user.')
+ );
+ });
+ });
+
+ describe('MicrosoftAdapter E2E Tests', () => {
+ beforeEach(async () => {
+ // Simulate reconfiguring the server with Microsoft auth options
+ await reconfigureServer({
+ auth: {
+ microsoft: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: false,
+ },
+ },
+ });
+ });
+
+ it('should authenticate user successfully using MicrosoftAdapter', async () => {
+ mockFetch([
+ {
+ url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken' }),
+ },
+ },
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ id: 'user123' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+ const user = await Parse.User.logInWith('microsoft', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle invalid code error gracefully', async () => {
+ mockFetch([
+ {
+ url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ method: 'POST',
+ response: { ok: false, statusText: 'Invalid code' },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'http://example.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('microsoft', { authData })).toBeRejectedWithError(
+ 'Microsoft API request failed.'
+ );
+ });
+
+ it('should handle error when fetching user data fails', async () => {
+ mockFetch([
+ {
+ url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken' }),
+ },
+ },
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: { ok: false, statusText: 'Unauthorized' },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('microsoft', { authData })).toBeRejectedWithError(
+ 'Microsoft API request failed.'
+ );
+ });
+
+ it('should allow insecure auth when enabled', async () => {
+
+ mockFetch([
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ id: 'user123',
+ }),
+ },
+ },
+ ])
+
+ await reconfigureServer({
+ auth: {
+ microsoft: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ const user = await Parse.User.logInWith('microsoft', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should reject insecure auth when user id does not match', async () => {
+
+ mockFetch([
+ {
+ url: 'https://graph.microsoft.com/v1.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ id: 'incorrectUser',
+ }),
+ },
+ },
+ ])
+
+ await reconfigureServer({
+ auth: {
+ microsoft: {
+ clientId: 'validClientId',
+ clientSecret: 'validClientSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ const authData = { access_token: 'validAccessToken', id: 'incorrectUserId' };
+ await expectAsync(Parse.User.logInWith('microsoft', { authData })).toBeRejectedWithError(
+ 'Microsoft auth is invalid for this user.'
+ );
+ });
+ });
+
+});
diff --git a/spec/Adapters/Auth/oauth2.spec.js b/spec/Adapters/Auth/oauth2.spec.js
new file mode 100644
index 0000000000..e5de368962
--- /dev/null
+++ b/spec/Adapters/Auth/oauth2.spec.js
@@ -0,0 +1,448 @@
+const OAuth2Adapter = require('../../../lib/Adapters/Auth/oauth2').default;
+
+describe('OAuth2Adapter', () => {
+ let adapter;
+
+ const validOptions = {
+ tokenIntrospectionEndpointUrl: 'https://provider.com/introspect',
+ useridField: 'sub',
+ appidField: 'aud',
+ appIds: ['valid-app-id'],
+ authorizationHeader: 'Bearer validAuthToken',
+ };
+
+ beforeEach(() => {
+ adapter = new OAuth2Adapter.constructor();
+ adapter.validateOptions(validOptions);
+ });
+
+ describe('validateAppId', () => {
+ it('should validate app ID successfully', async () => {
+ const authData = { access_token: 'validAccessToken' };
+ const mockResponse = {
+ [validOptions.appidField]: 'valid-app-id',
+ };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.validateAppId(validOptions.appIds, authData, validOptions)
+ ).toBeResolved();
+ });
+
+ it('should throw an error if app ID is invalid', async () => {
+ const authData = { access_token: 'validAccessToken' };
+ const mockResponse = {
+ [validOptions.appidField]: 'invalid-app-id',
+ };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.validateAppId(validOptions.appIds, authData, validOptions)
+ ).toBeRejectedWithError('OAuth2: Invalid app ID.');
+ });
+ });
+
+ describe('validateAuthData', () => {
+ it('should validate auth data successfully', async () => {
+ const authData = { id: 'user-id', access_token: 'validAccessToken' };
+ const mockResponse = {
+ active: true,
+ [validOptions.useridField]: 'user-id',
+ };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.validateAuthData(authData, null, validOptions)
+ ).toBeResolvedTo({});
+ });
+
+ it('should throw an error if the token is inactive', async () => {
+ const authData = { id: 'user-id', access_token: 'validAccessToken' };
+ const mockResponse = { active: false };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.validateAuthData(authData, null, validOptions)
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.'));
+ });
+
+ it('should throw an error if user ID does not match', async () => {
+ const authData = { id: 'user-id', access_token: 'validAccessToken' };
+ const mockResponse = {
+ active: true,
+ [validOptions.useridField]: 'different-user-id',
+ };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.validateAuthData(authData, null, validOptions)
+ ).toBeRejectedWithError('OAuth2 access token is invalid for this user.');
+ });
+
+ it('should default useridField to sub and reject mismatched user ID', async () => {
+ const adapterNoUseridField = new OAuth2Adapter.constructor();
+ adapterNoUseridField.validateOptions({
+ tokenIntrospectionEndpointUrl: 'https://provider.example.com/introspect',
+ });
+
+ const authData = { id: 'victim-user-id', access_token: 'attackerToken' };
+ const mockResponse = {
+ active: true,
+ sub: 'attacker-user-id',
+ };
+
+ mockFetch([
+ {
+ url: 'https://provider.example.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapterNoUseridField.validateAuthData(authData, null, {})
+ ).toBeRejectedWithError('OAuth2 access token is invalid for this user.');
+ });
+
+ it('should default useridField to sub and accept matching user ID', async () => {
+ const adapterNoUseridField = new OAuth2Adapter.constructor();
+ adapterNoUseridField.validateOptions({
+ tokenIntrospectionEndpointUrl: 'https://provider.example.com/introspect',
+ });
+
+ const authData = { id: 'user-id', access_token: 'validAccessToken' };
+ const mockResponse = {
+ active: true,
+ sub: 'user-id',
+ };
+
+ mockFetch([
+ {
+ url: 'https://provider.example.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapterNoUseridField.validateAuthData(authData, null, {})
+ ).toBeResolvedTo({});
+ });
+ });
+
+ describe('requestTokenInfo', () => {
+ it('should fetch token info successfully', async () => {
+ const mockResponse = { active: true };
+
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ const result = await adapter.requestTokenInfo(
+ 'validAccessToken',
+ validOptions
+ );
+
+ expect(result).toEqual(mockResponse);
+ });
+
+ it('should throw an error if the introspection endpoint URL is missing', async () => {
+ const options = { ...validOptions, tokenIntrospectionEndpointUrl: null };
+
+ expect(
+ () => adapter.validateOptions(options)
+ ).toThrow(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 token introspection endpoint URL is missing.'));
+ });
+
+ it('should throw an error if the response is not ok', async () => {
+ mockFetch([
+ {
+ url: validOptions.tokenIntrospectionEndpointUrl,
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.requestTokenInfo('invalidAccessToken')
+ ).toBeRejectedWithError('OAuth2 token introspection request failed.');
+ });
+ });
+
+ describe('OAuth2Adapter E2E Tests', () => {
+ beforeEach(async () => {
+ // Simulate reconfiguring the server with OAuth2 auth options
+ await reconfigureServer({
+ auth: {
+ mockOauth: {
+ tokenIntrospectionEndpointUrl: 'https://provider.com/introspect',
+ useridField: 'sub',
+ appidField: 'aud',
+ appIds: ['valid-app-id'],
+ authorizationHeader: 'Bearer validAuthToken',
+ oauth2: true
+ },
+ },
+ });
+ });
+
+ it('should validate and authenticate user successfully', async () => {
+ mockFetch([
+ {
+ url: 'https://provider.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'user123',
+ aud: 'valid-app-id',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ const user = await Parse.User.logInWith('mockOauth', { authData });
+
+ expect(user.id).toBeDefined();
+ expect(user.get('authData').mockOauth.id).toEqual('user123');
+ });
+
+ it('should reject authentication for inactive token', async () => {
+ mockFetch([
+ {
+ url: 'https://provider.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ active: false, aud: ['valid-app-id'] }),
+ },
+ },
+ ]);
+
+ const authData = { access_token: 'inactiveToken', id: 'user123' };
+ await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.')
+ );
+ });
+
+ it('should reject authentication for mismatched user ID', async () => {
+ mockFetch([
+ {
+ url: 'https://provider.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'different-user',
+ aud: 'valid-app-id',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.')
+ );
+ });
+
+ it('should reject authentication for invalid app ID', async () => {
+ mockFetch([
+ {
+ url: 'https://provider.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'user123',
+ aud: 'invalid-app-id',
+ }),
+ },
+ },
+ ]);
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWithError(
+ 'OAuth2: Invalid app ID.'
+ );
+ });
+
+ it('should send the correct access token to the introspection endpoint during app ID validation', async () => {
+ const capturedTokens = [];
+ const originalFetch = global.fetch;
+ try {
+ global.fetch = async (url, options) => {
+ if (typeof url === 'string' && url === 'https://provider.com/introspect') {
+ const body = options?.body?.toString() || '';
+ const token = new URLSearchParams(body).get('token');
+ capturedTokens.push(token);
+ return {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'user123',
+ aud: 'valid-app-id',
+ }),
+ };
+ }
+ return originalFetch(url, options);
+ };
+
+ const authData = { access_token: 'myRealAccessToken', id: 'user123' };
+ const user = await Parse.User.logInWith('mockOauth', { authData });
+ expect(user.id).toBeDefined();
+
+ // With appidField configured, validateAppId and validateAuthData both call requestTokenInfo.
+ // Both should receive the actual access token, not 'undefined' from argument mismatch.
+ expect(capturedTokens.length).toBeGreaterThanOrEqual(2);
+ for (const token of capturedTokens) {
+ expect(token).toBe('myRealAccessToken');
+ }
+ } finally {
+ global.fetch = originalFetch;
+ }
+ });
+
+ it('should reject account takeover when useridField is omitted and attacker uses their own token with victim ID', async () => {
+ await reconfigureServer({
+ auth: {
+ mockOauth: {
+ tokenIntrospectionEndpointUrl: 'https://provider.example.com/introspect',
+ authorizationHeader: 'Bearer validAuthToken',
+ oauth2: true,
+ },
+ },
+ });
+
+ // Victim signs up with their own valid token
+ mockFetch([
+ {
+ url: 'https://provider.example.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'victim-sub-id',
+ }),
+ },
+ },
+ ]);
+
+ const victimAuthData = { access_token: 'victimToken', id: 'victim-sub-id' };
+ const victim = await Parse.User.logInWith('mockOauth', { authData: victimAuthData });
+ expect(victim.id).toBeDefined();
+
+ // Attacker tries to log in with their own valid token but claims victim's ID
+ mockFetch([
+ {
+ url: 'https://provider.example.com/introspect',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({
+ active: true,
+ sub: 'attacker-sub-id',
+ }),
+ },
+ },
+ ]);
+
+ const attackerAuthData = { access_token: 'attackerToken', id: 'victim-sub-id' };
+ await expectAsync(Parse.User.logInWith('mockOauth', { authData: attackerAuthData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 access token is invalid for this user.')
+ );
+ });
+
+ it('should handle error when token introspection endpoint is missing', async () => {
+ await reconfigureServer({
+ auth: {
+ mockOauth: {
+ tokenIntrospectionEndpointUrl: null,
+ useridField: 'sub',
+ appidField: 'aud',
+ appIds: ['valid-app-id'],
+ authorizationHeader: 'Bearer validAuthToken',
+ oauth2: true
+ },
+ },
+ });
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ await expectAsync(Parse.User.logInWith('mockOauth', { authData })).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'OAuth2 token introspection endpoint URL is missing.')
+ );
+ });
+ });
+
+});
diff --git a/spec/Adapters/Auth/qq.spec.js b/spec/Adapters/Auth/qq.spec.js
new file mode 100644
index 0000000000..1e67e18941
--- /dev/null
+++ b/spec/Adapters/Auth/qq.spec.js
@@ -0,0 +1,252 @@
+const QqAdapter = require('../../../lib/Adapters/Auth/qq').default;
+
+describe('QqAdapter', () => {
+ let adapter;
+
+ beforeEach(() => {
+ adapter = new QqAdapter.constructor();
+ });
+
+ describe('getUserFromAccessToken', () => {
+ it('should fetch user data successfully', async () => {
+ const mockResponse = `callback({"client_id":"validAppId","openid":"user123"})`;
+
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ const result = await adapter.getUserFromAccessToken('validAccessToken');
+
+ expect(result).toEqual({ client_id: 'validAppId', openid: 'user123' });
+ });
+
+ it('should throw an error if the API request fails', async () => {
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/me',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.getUserFromAccessToken('invalidAccessToken')
+ ).toBeRejectedWithError('qq API request failed.');
+ });
+ });
+
+ describe('getAccessTokenFromCode', () => {
+ it('should fetch access token successfully', async () => {
+ const mockResponse = `callback({"access_token":"validAccessToken","expires_in":3600,"refresh_token":"refreshToken"})`;
+
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ const result = await adapter.getAccessTokenFromCode({
+ code: 'validCode',
+ redirect_uri: 'https://your-redirect-uri.com/callback',
+ });
+
+ expect(result).toBe('validAccessToken');
+ });
+
+ it('should throw an error if the API request fails', async () => {
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ await expectAsync(
+ adapter.getAccessTokenFromCode({
+ code: 'invalidCode',
+ redirect_uri: 'https://your-redirect-uri.com/callback',
+ })
+ ).toBeRejectedWithError('qq API request failed.');
+ });
+ });
+
+ describe('parseResponseData', () => {
+ it('should parse valid callback response data', () => {
+ const response = `callback({"key":"value"})`;
+ const result = adapter.parseResponseData(response);
+
+ expect(result).toEqual({ key: 'value' });
+ });
+
+ it('should throw an error if the response data is invalid', () => {
+ const response = 'invalid response';
+
+ expect(() => adapter.parseResponseData(response)).toThrowError(
+ 'qq auth is invalid for this user.'
+ );
+ });
+ });
+
+ describe('QqAdapter E2E Test', () => {
+ beforeEach(async () => {
+ await reconfigureServer({
+ auth: {
+ qq: {
+ clientId: 'validAppId',
+ clientSecret: 'validAppSecret',
+ },
+ },
+ });
+ });
+
+ it('should log in user using Qq adapter successfully', async () => {
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () =>
+ Promise.resolve(
+ `callback({"access_token":"mockAccessToken","expires_in":3600})`
+ ),
+ },
+ },
+ {
+ url: 'https://graph.qq.com/oauth2.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () =>
+ Promise.resolve(
+ `callback({"client_id":"validAppId","openid":"user123"})`
+ ),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'https://your-redirect-uri.com/callback' };
+ const user = await Parse.User.logInWith('qq', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle error when Qq returns invalid code', async () => {
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Invalid code',
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'https://your-redirect-uri.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('qq', { authData })).toBeRejectedWithError(
+ 'qq API request failed.'
+ );
+ });
+
+ it('should handle error when Qq returns invalid user data', async () => {
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () =>
+ Promise.resolve(
+ `callback({"access_token":"mockAccessToken","expires_in":3600})`
+ ),
+ },
+ },
+ {
+ url: 'https://graph.qq.com/oauth2.0/me',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'https://your-redirect-uri.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('qq', { authData })).toBeRejectedWithError(
+ 'qq API request failed.'
+ );
+ });
+
+ it('e2e secure does not support insecure payload', async () => {
+ mockFetch();
+ const authData = { id: 'mockUserId', access_token: 'mockAccessToken' };
+ await expectAsync(Parse.User.logInWith('qq', { authData })).toBeRejectedWithError(
+ 'qq code is required.'
+ );
+ });
+
+ it('e2e insecure does support secure payload', async () => {
+ await reconfigureServer({
+ auth: {
+ qq: {
+ appId: 'validAppId',
+ appSecret: 'validAppSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ mockFetch([
+ {
+ url: 'https://graph.qq.com/oauth2.0/token',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () =>
+ Promise.resolve(
+ `callback({"access_token":"mockAccessToken","expires_in":3600})`
+ ),
+ },
+ },
+ {
+ url: 'https://graph.qq.com/oauth2.0/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ text: () =>
+ Promise.resolve(
+ `callback({"client_id":"validAppId","openid":"user123"})`
+ ),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'https://your-redirect-uri.com/callback' };
+ const user = await Parse.User.logInWith('qq', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/spotify.spec.js b/spec/Adapters/Auth/spotify.spec.js
new file mode 100644
index 0000000000..b3c6a5ef6f
--- /dev/null
+++ b/spec/Adapters/Auth/spotify.spec.js
@@ -0,0 +1,113 @@
+const SpotifyAdapter = require('../../../lib/Adapters/Auth/spotify').default;
+
+describe('SpotifyAdapter', () => {
+ let adapter;
+
+ beforeEach(() => {
+ adapter = new SpotifyAdapter.constructor();
+ });
+
+ describe('getUserFromAccessToken', () => {
+ it('should fetch user data successfully', async () => {
+ const mockResponse = {
+ id: 'spotifyUser123',
+ };
+
+ mockFetch([
+ {
+ url: 'https://api.spotify.com/v1/me',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ const result = await adapter.getUserFromAccessToken('validAccessToken');
+
+ expect(result).toEqual({ id: 'spotifyUser123' });
+ });
+
+ it('should throw an error if the API request fails', async () => {
+ mockFetch([
+ {
+ url: 'https://api.spotify.com/v1/me',
+ method: 'GET',
+ response: {
+ ok: false,
+ statusText: 'Unauthorized',
+ },
+ },
+ ]);
+
+ await expectAsync(adapter.getUserFromAccessToken('invalidAccessToken')).toBeRejectedWithError(
+ 'Spotify API request failed.'
+ );
+ });
+ });
+
+ describe('getAccessTokenFromCode', () => {
+ it('should fetch access token successfully', async () => {
+ const mockResponse = {
+ access_token: 'validAccessToken',
+ expires_in: 3600,
+ refresh_token: 'refreshToken',
+ };
+
+ mockFetch([
+ {
+ url: 'https://accounts.spotify.com/api/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve(mockResponse),
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'validCode',
+ redirect_uri: 'https://your-redirect-uri.com/callback',
+ code_verifier: 'validCodeVerifier',
+ };
+
+ const result = await adapter.getAccessTokenFromCode(authData);
+
+ expect(result).toEqual(mockResponse);
+ });
+
+ it('should throw an error if authData is missing required fields', async () => {
+ const authData = {
+ redirect_uri: 'https://your-redirect-uri.com/callback',
+ };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Spotify auth configuration authData.code and/or authData.redirect_uri and/or authData.code_verifier.'
+ );
+ });
+
+ it('should throw an error if the API request fails', async () => {
+ mockFetch([
+ {
+ url: 'https://accounts.spotify.com/api/token',
+ method: 'POST',
+ response: {
+ ok: false,
+ statusText: 'Bad Request',
+ },
+ },
+ ]);
+
+ const authData = {
+ code: 'invalidCode',
+ redirect_uri: 'https://your-redirect-uri.com/callback',
+ code_verifier: 'invalidCodeVerifier',
+ };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWithError(
+ 'Spotify API request failed.'
+ );
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/twitter.spec.js b/spec/Adapters/Auth/twitter.spec.js
new file mode 100644
index 0000000000..2869ff4121
--- /dev/null
+++ b/spec/Adapters/Auth/twitter.spec.js
@@ -0,0 +1,120 @@
+const TwitterAuthAdapter = require('../../../lib/Adapters/Auth/twitter').default;
+
+describe('TwitterAuthAdapter', function () {
+ let adapter;
+ const validOptions = {
+ consumer_key: 'validConsumerKey',
+ consumer_secret: 'validConsumerSecret',
+ };
+
+ beforeEach(function () {
+ adapter = new TwitterAuthAdapter.constructor();
+ });
+
+ describe('Test configuration errors', function () {
+ it('should throw an error when options are missing', function () {
+ expect(() => adapter.validateOptions()).toThrowError('Twitter auth options are required.');
+ });
+
+ it('should throw an error when consumer_key and consumer_secret are missing for secure auth', function () {
+ const options = { enableInsecureAuth: false };
+ expect(() => adapter.validateOptions(options)).toThrowError(
+ 'Consumer key and secret are required for secure Twitter auth.'
+ );
+ });
+
+ it('should not throw an error when valid options are provided', function () {
+ expect(() => adapter.validateOptions(validOptions)).not.toThrow();
+ });
+ });
+
+ describe('Validate Insecure Auth', function () {
+ it('should throw an error if oauth_token or oauth_token_secret are missing', async function () {
+ const authData = { oauth_token: 'validToken' }; // Missing oauth_token_secret
+ await expectAsync(adapter.validateInsecureAuth(authData, validOptions)).toBeRejectedWithError(
+ 'Twitter insecure auth requires oauth_token and oauth_token_secret.'
+ );
+ });
+
+ it('should validate insecure auth successfully when data matches', async function () {
+ spyOn(adapter, 'request').and.returnValue(
+ Promise.resolve({
+ json: () => Promise.resolve({ id: 'validUserId' }),
+ })
+ );
+
+ const authData = {
+ id: 'validUserId',
+ oauth_token: 'validToken',
+ oauth_token_secret: 'validSecret',
+ };
+ await expectAsync(adapter.validateInsecureAuth(authData, validOptions)).toBeResolved();
+ });
+
+ it('should throw an error when user ID does not match', async function () {
+ spyOn(adapter, 'request').and.returnValue(
+ Promise.resolve({
+ json: () => Promise.resolve({ id: 'invalidUserId' }),
+ })
+ );
+
+ const authData = {
+ id: 'validUserId',
+ oauth_token: 'validToken',
+ oauth_token_secret: 'validSecret',
+ };
+ await expectAsync(adapter.validateInsecureAuth(authData, validOptions)).toBeRejectedWithError(
+ 'Twitter auth is invalid for this user.'
+ );
+ });
+ });
+
+ describe('End-to-End Tests', function () {
+ beforeEach(async function () {
+ await reconfigureServer({
+ auth: {
+ twitter: validOptions,
+ }
+ })
+ });
+
+ it('should authenticate user successfully using validateAuthData', async function () {
+ spyOn(adapter, 'exchangeAccessToken').and.returnValue(
+ Promise.resolve({ oauth_token: 'validToken', user_id: 'validUserId' })
+ );
+
+ const authData = {
+ oauth_token: 'validToken',
+ oauth_verifier: 'validVerifier',
+ };
+ await expectAsync(adapter.validateAuthData(authData, validOptions)).toBeResolved();
+ expect(authData.id).toBe('validUserId');
+ expect(authData.auth_token).toBe('validToken');
+ });
+
+ it('should handle multiple configurations and validate successfully', async function () {
+ const authData = {
+ consumer_key: 'validConsumerKey',
+ oauth_token: 'validToken',
+ oauth_token_secret: 'validSecret',
+ };
+
+ const optionsArray = [
+ { consumer_key: 'invalidKey', consumer_secret: 'invalidSecret' },
+ validOptions,
+ ];
+
+ const selectedOption = adapter.handleMultipleConfigurations(authData, optionsArray);
+ expect(selectedOption).toEqual(validOptions);
+ });
+
+ it('should throw an error when no matching configuration is found', function () {
+ const authData = { consumer_key: 'missingKey' };
+ const optionsArray = [validOptions];
+
+ expect(() => adapter.handleMultipleConfigurations(authData, optionsArray)).toThrowError(
+ 'Twitter auth is invalid for this user.'
+ );
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/wechat.spec.js b/spec/Adapters/Auth/wechat.spec.js
new file mode 100644
index 0000000000..43518ec0df
--- /dev/null
+++ b/spec/Adapters/Auth/wechat.spec.js
@@ -0,0 +1,236 @@
+const WeChatAdapter = require('../../../lib/Adapters/Auth/wechat').default;
+
+describe('WeChatAdapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new WeChatAdapter.constructor();
+ });
+
+ describe('Test getUserFromAccessToken', function () {
+ it('should fetch user successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=validToken&openid=validOpenId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ errcode: 0, id: 'validUserId' }),
+ },
+ },
+ ]);
+
+ const user = await adapter.getUserFromAccessToken('validToken', { id: 'validOpenId' });
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.weixin.qq.com/sns/auth?access_token=validToken&openid=validOpenId',
+ jasmine.any(Object)
+ );
+ expect(user).toEqual({ errcode: 0, id: 'validUserId' });
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=invalidToken&openid=undefined',
+ method: 'GET',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40013, errmsg: 'Invalid token' }),
+ },
+ },
+ ]);
+
+ await expectAsync(adapter.getUserFromAccessToken('invalidToken', 'invalidOpenId')).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'WeChat auth is invalid for this user.' })
+ );
+ });
+ });
+
+ describe('Test getAccessTokenFromCode', function () {
+ it('should fetch access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validToken', errcode: 0 }),
+ },
+ },
+ ]);
+
+ adapter.validateOptions({ clientId: 'validAppId', clientSecret: 'validAppSecret' });
+ const authData = { code: 'validCode' };
+ const token = await adapter.getAccessTokenFromCode(authData);
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code',
+ jasmine.any(Object)
+ );
+ expect(token).toEqual('validToken');
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=invalidCode&grant_type=authorization_code',
+ method: 'GET',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40029, errmsg: 'Invalid code' }),
+ },
+ },
+ ]);
+ adapter.validateOptions({ clientId: 'validAppId', clientSecret: 'validAppSecret' });
+
+ const authData = { code: 'invalidCode' };
+
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'WeChat auth is invalid for this user.' })
+ );
+ });
+ });
+
+ describe('WeChatAdapter E2E Tests', function () {
+ beforeEach(async () => {
+ await reconfigureServer({
+ auth: {
+ wechat: {
+ clientId: 'validAppId',
+ clientSecret: 'validAppSecret',
+ enableInsecureAuth: false,
+ },
+ },
+ });
+ });
+
+ it('should authenticate user successfully using WeChatAdapter', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken', openid: 'user123', errcode: 0 }),
+ },
+ },
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=validAccessToken&openid=user123',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ errcode: 0, id: 'user123' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+ const user = await Parse.User.logInWith('wechat', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle invalid code error gracefully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=invalidCode&grant_type=authorization_code',
+ method: 'GET',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40029, errmsg: 'Invalid code' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'http://example.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('wechat', { authData })).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'WeChat auth is invalid for this user.' })
+ );
+ });
+
+ it('should handle error when fetching user data fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken', openid: 'user123', errcode: 0 }),
+ },
+ },
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=validAccessToken&openid=user123',
+ method: 'GET',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40013, errmsg: 'Invalid token' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+
+ await expectAsync(Parse.User.logInWith('wechat', { authData })).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'WeChat auth is invalid for this user.' })
+ );
+ });
+
+ it('should allow insecure auth when enabled', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=validAccessToken&openid=user123',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ errcode: 0, id: 'user123' }),
+ },
+ },
+ ]);
+
+ await reconfigureServer({
+ auth: {
+ wechat: {
+ appId: 'validAppId',
+ appSecret: 'validAppSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ const authData = { access_token: 'validAccessToken', id: 'user123' };
+ const user = await Parse.User.logInWith('wechat', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should reject insecure auth when user id does not match', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weixin.qq.com/sns/auth?access_token=validAccessToken&openid=incorrectUserId',
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ errcode: 0, id: 'incorrectUser' }),
+ },
+ },
+ ]);
+
+ await reconfigureServer({
+ auth: {
+ wechat: {
+ appId: 'validAppId',
+ appSecret: 'validAppSecret',
+ enableInsecureAuth: true,
+ },
+ },
+ });
+
+ const authData = { access_token: 'validAccessToken', id: 'incorrectUserId' };
+ await expectAsync(Parse.User.logInWith('wechat', { authData })).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'WeChat auth is invalid for this user.' })
+ );
+ });
+ });
+});
diff --git a/spec/Adapters/Auth/weibo.spec.js b/spec/Adapters/Auth/weibo.spec.js
new file mode 100644
index 0000000000..685739e663
--- /dev/null
+++ b/spec/Adapters/Auth/weibo.spec.js
@@ -0,0 +1,204 @@
+const WeiboAdapter = require('../../../lib/Adapters/Auth/weibo').default;
+
+describe('WeiboAdapter', function () {
+ let adapter;
+
+ beforeEach(function () {
+ adapter = new WeiboAdapter.constructor();
+ });
+
+ describe('Test configuration errors', function () {
+ it('should throw error if code or redirect_uri is missing', async function () {
+ const invalidAuthData = [
+ {},
+ { code: 'validCode' },
+ { redirect_uri: 'http://example.com/callback' },
+ ];
+
+ for (const authData of invalidAuthData) {
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWith(
+ jasmine.objectContaining({
+ message: 'Weibo auth requires code and redirect_uri to be sent.',
+ })
+ );
+ }
+ });
+ });
+
+ describe('Test getUserFromAccessToken', function () {
+ it('should fetch user successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/get_token_info',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ uid: 'validUserId' }),
+ },
+ },
+ ]);
+
+ const authData = { id: 'validUserId' };
+ const user = await adapter.getUserFromAccessToken('validToken', authData);
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.weibo.com/oauth2/get_token_info',
+ jasmine.objectContaining({
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ })
+ );
+ expect(user).toEqual({ id: 'validUserId' });
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/get_token_info',
+ method: 'POST',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const authData = { id: 'invalidUserId' };
+ await expectAsync(adapter.getUserFromAccessToken('invalidToken', authData)).toBeRejectedWith(
+ jasmine.objectContaining({
+ message: 'Weibo auth is invalid for this user.',
+ })
+ );
+ });
+ });
+
+ describe('Test getAccessTokenFromCode', function () {
+ it('should fetch access token successfully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validToken', uid: 'validUserId' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+ const token = await adapter.getAccessTokenFromCode(authData);
+
+ expect(global.fetch).toHaveBeenCalledWith(
+ 'https://api.weibo.com/oauth2/access_token',
+ jasmine.objectContaining({
+ method: 'POST',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ })
+ );
+ expect(token).toEqual('validToken');
+ });
+
+ it('should throw error for invalid response', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/access_token',
+ method: 'POST',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40029 }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'http://example.com/callback' };
+ await expectAsync(adapter.getAccessTokenFromCode(authData)).toBeRejectedWith(
+ jasmine.objectContaining({
+ message: 'Weibo auth is invalid for this user.',
+ })
+ );
+ });
+ });
+
+ describe('WeiboAdapter E2E Tests', function () {
+ beforeEach(async () => {
+ await reconfigureServer({
+ auth: {
+ weibo: {
+ clientId: 'validAppId',
+ clientSecret: 'validAppSecret',
+ },
+ }
+ });
+ });
+
+ it('should authenticate user successfully using WeiboAdapter', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken', uid: 'user123' }),
+ },
+ },
+ {
+ url: 'https://api.weibo.com/oauth2/get_token_info',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ uid: 'user123' }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+ const user = await Parse.User.logInWith('weibo', { authData });
+
+ expect(user.id).toBeDefined();
+ });
+
+ it('should handle invalid code error gracefully', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/access_token',
+ method: 'POST',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({ errcode: 40029 }),
+ },
+ },
+ ]);
+
+ const authData = { code: 'invalidCode', redirect_uri: 'http://example.com/callback' };
+ await expectAsync(Parse.User.logInWith('weibo', { authData })).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'Weibo auth is invalid for this user.' })
+ );
+ });
+
+ it('should handle error when fetching user data fails', async function () {
+ mockFetch([
+ {
+ url: 'https://api.weibo.com/oauth2/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'validAccessToken', uid: 'user123' }),
+ },
+ },
+ {
+ url: 'https://api.weibo.com/oauth2/get_token_info',
+ method: 'POST',
+ response: {
+ ok: false,
+ json: () => Promise.resolve({}),
+ },
+ },
+ ]);
+
+ const authData = { code: 'validCode', redirect_uri: 'http://example.com/callback' };
+ await expectAsync(Parse.User.logInWith('weibo', { authData })).toBeRejectedWith(
+ jasmine.objectContaining({ message: 'Weibo auth is invalid for this user.' })
+ );
+ });
+ });
+});
diff --git a/spec/AggregateRouter.spec.js b/spec/AggregateRouter.spec.js
new file mode 100644
index 0000000000..96aedcc313
--- /dev/null
+++ b/spec/AggregateRouter.spec.js
@@ -0,0 +1,174 @@
+const AggregateRouter = require('../lib/Routers/AggregateRouter').AggregateRouter;
+
+describe('AggregateRouter', () => {
+ it('get pipeline from Array', () => {
+ const body = [
+ {
+ $group: { _id: {} },
+ },
+ ];
+ const expected = [{ $group: { _id: {} } }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('get pipeline from Object', () => {
+ const body = {
+ $group: { _id: {} },
+ };
+ const expected = [{ $group: { _id: {} } }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('get pipeline from Pipeline Operator (Array)', () => {
+ const body = {
+ pipeline: [
+ {
+ $group: { _id: {} },
+ },
+ ],
+ };
+ const expected = [{ $group: { _id: {} } }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('get pipeline from Pipeline Operator (Object)', () => {
+ const body = {
+ pipeline: {
+ $group: { _id: {} },
+ },
+ };
+ const expected = [{ $group: { _id: {} } }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('get pipeline fails multiple keys in Array stage ', () => {
+ const body = [
+ {
+ $group: { _id: {} },
+ $match: { name: 'Test' },
+ },
+ ];
+ expect(() => AggregateRouter.getPipeline(body)).toThrow(
+ new Parse.Error(
+ Parse.Error.INVALID_QUERY,
+ 'Pipeline stages should only have one key but found $group, $match.'
+ )
+ );
+ });
+
+ it('get pipeline fails multiple keys in Pipeline Operator Array stage ', () => {
+ const body = {
+ pipeline: [
+ {
+ $group: { _id: {} },
+ $match: { name: 'Test' },
+ },
+ ],
+ };
+ expect(() => AggregateRouter.getPipeline(body)).toThrow(
+ new Parse.Error(
+ Parse.Error.INVALID_QUERY,
+ 'Pipeline stages should only have one key but found $group, $match.'
+ )
+ );
+ });
+
+ it('get search pipeline from Pipeline Operator (Array)', () => {
+ const body = {
+ pipeline: {
+ $search: {},
+ },
+ };
+ const expected = [{ $search: {} }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('support stage name starting with `$`', () => {
+ const body = {
+ $match: { someKey: 'whatever' },
+ };
+ const expected = [{ $match: { someKey: 'whatever' } }];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('support nested stage names starting with `$`', () => {
+ const body = [
+ {
+ $lookup: {
+ from: 'ACollection',
+ let: { id: '_id' },
+ as: 'results',
+ pipeline: [
+ {
+ $match: {
+ $expr: {
+ $eq: ['$_id', '$$id'],
+ },
+ },
+ },
+ ],
+ },
+ },
+ ];
+ const expected = [
+ {
+ $lookup: {
+ from: 'ACollection',
+ let: { id: '_id' },
+ as: 'results',
+ pipeline: [
+ {
+ $match: {
+ $expr: {
+ $eq: ['$_id', '$$id'],
+ },
+ },
+ },
+ ],
+ },
+ },
+ ];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('support the use of `_id` in stages', () => {
+ const body = [
+ { $match: { _id: 'randomId' } },
+ { $sort: { _id: -1 } },
+ { $addFields: { _id: 1 } },
+ { $group: { _id: {} } },
+ { $project: { _id: 0 } },
+ ];
+ const expected = [
+ { $match: { _id: 'randomId' } },
+ { $sort: { _id: -1 } },
+ { $addFields: { _id: 1 } },
+ { $group: { _id: {} } },
+ { $project: { _id: 0 } },
+ ];
+ const result = AggregateRouter.getPipeline(body);
+ expect(result).toEqual(expected);
+ });
+
+ it('should throw with invalid stage', () => {
+ expect(() => AggregateRouter.getPipeline([{ foo: 'bar' }])).toThrow(
+ new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage 'foo'.`)
+ );
+ });
+
+ it('should throw with invalid group', () => {
+ expect(() => AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }])).toThrow(
+ new Parse.Error(
+ Parse.Error.INVALID_QUERY,
+ `Cannot use 'objectId' in aggregation stage $group.`
+ )
+ );
+ });
+});
diff --git a/spec/Analytics.spec.js b/spec/Analytics.spec.js
new file mode 100644
index 0000000000..049a2795c8
--- /dev/null
+++ b/spec/Analytics.spec.js
@@ -0,0 +1,69 @@
+const analyticsAdapter = {
+ appOpened: function () {},
+ trackEvent: function () {},
+};
+
+describe('AnalyticsController', () => {
+ it('should track a simple event', done => {
+ spyOn(analyticsAdapter, 'trackEvent').and.callThrough();
+ reconfigureServer({
+ analyticsAdapter,
+ })
+ .then(() => {
+ return Parse.Analytics.track('MyEvent', {
+ key: 'value',
+ count: '0',
+ });
+ })
+ .then(
+ () => {
+ expect(analyticsAdapter.trackEvent).toHaveBeenCalled();
+ const lastCall = analyticsAdapter.trackEvent.calls.first();
+ const args = lastCall.args;
+ expect(args[0]).toEqual('MyEvent');
+ expect(args[1]).toEqual({
+ dimensions: {
+ key: 'value',
+ count: '0',
+ },
+ });
+ done();
+ },
+ err => {
+ fail(JSON.stringify(err));
+ done();
+ }
+ );
+ });
+
+ it('should track a app opened event', done => {
+ spyOn(analyticsAdapter, 'appOpened').and.callThrough();
+ reconfigureServer({
+ analyticsAdapter,
+ })
+ .then(() => {
+ return Parse.Analytics.track('AppOpened', {
+ key: 'value',
+ count: '0',
+ });
+ })
+ .then(
+ () => {
+ expect(analyticsAdapter.appOpened).toHaveBeenCalled();
+ const lastCall = analyticsAdapter.appOpened.calls.first();
+ const args = lastCall.args;
+ expect(args[0]).toEqual({
+ dimensions: {
+ key: 'value',
+ count: '0',
+ },
+ });
+ done();
+ },
+ err => {
+ fail(JSON.stringify(err));
+ done();
+ }
+ );
+ });
+});
diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js
new file mode 100644
index 0000000000..96b8f2459f
--- /dev/null
+++ b/spec/AudienceRouter.spec.js
@@ -0,0 +1,444 @@
+const auth = require('../lib/Auth');
+const Config = require('../lib/Config');
+const rest = require('../lib/rest');
+const request = require('../lib/request');
+const AudiencesRouter = require('../lib/Routers/AudiencesRouter').AudiencesRouter;
+
+describe('AudiencesRouter', () => {
+ let loggerErrorSpy;
+
+ beforeEach(() => {
+ const logger = require('../lib/logger').default;
+ loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
+ });
+
+ it('uses find condition from request.body', done => {
+ const config = Config.get('test');
+ const androidAudienceRequest = {
+ name: 'Android Users',
+ query: '{ "test": "android" }',
+ };
+ const iosAudienceRequest = {
+ name: 'Iphone Users',
+ query: '{ "test": "ios" }',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {
+ where: {
+ query: '{ "test": "android" }',
+ },
+ },
+ query: {},
+ info: {},
+ };
+
+ const router = new AudiencesRouter();
+ rest
+ .create(config, auth.master(config), '_Audience', androidAudienceRequest)
+ .then(() => {
+ return rest.create(config, auth.master(config), '_Audience', iosAudienceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const results = res.response.results;
+ expect(results.length).toEqual(1);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
+ });
+
+ it('uses find condition from request.query', done => {
+ const config = Config.get('test');
+ const androidAudienceRequest = {
+ name: 'Android Users',
+ query: '{ "test": "android" }',
+ };
+ const iosAudienceRequest = {
+ name: 'Iphone Users',
+ query: '{ "test": "ios" }',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ where: {
+ query: '{ "test": "android" }',
+ },
+ },
+ info: {},
+ };
+
+ const router = new AudiencesRouter();
+ rest
+ .create(config, auth.master(config), '_Audience', androidAudienceRequest)
+ .then(() => {
+ return rest.create(config, auth.master(config), '_Audience', iosAudienceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const results = res.response.results;
+ expect(results.length).toEqual(1);
+ done();
+ })
+ .catch(err => {
+ fail(err);
+ done();
+ });
+ });
+
+ it('query installations with limit = 0', done => {
+ const config = Config.get('test');
+ const androidAudienceRequest = {
+ name: 'Android Users',
+ query: '{ "test": "android" }',
+ };
+ const iosAudienceRequest = {
+ name: 'Iphone Users',
+ query: '{ "test": "ios" }',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ limit: 0,
+ },
+ info: {},
+ };
+
+ Config.get('test');
+ const router = new AudiencesRouter();
+ rest
+ .create(config, auth.master(config), '_Audience', androidAudienceRequest)
+ .then(() => {
+ return rest.create(config, auth.master(config), '_Audience', iosAudienceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(0);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
+ });
+
+ it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
+ const config = Config.get('test');
+ const androidAudienceRequest = {
+ name: 'Android Users',
+ query: '{ "test": "android" }',
+ };
+ const iosAudienceRequest = {
+ name: 'Iphone Users',
+ query: '{ "test": "ios" }',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ count: 1,
+ },
+ info: {},
+ };
+
+ const router = new AudiencesRouter();
+ rest
+ .create(config, auth.master(config), '_Audience', androidAudienceRequest)
+ .then(() => rest.create(config, auth.master(config), '_Audience', iosAudienceRequest))
+ .then(() => router.handleFind(request))
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(2);
+ expect(response.count).toEqual(2);
+ done();
+ })
+ .catch(error => {
+ fail(JSON.stringify(error));
+ done();
+ });
+ });
+
+ it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => {
+ const config = Config.get('test');
+ const androidAudienceRequest = {
+ name: 'Android Users',
+ query: '{ "test": "android" }',
+ };
+ const iosAudienceRequest = {
+ name: 'Iphone Users',
+ query: '{ "test": "ios" }',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ limit: 0,
+ count: 1,
+ },
+ info: {},
+ };
+
+ const router = new AudiencesRouter();
+ rest
+ .create(config, auth.master(config), '_Audience', androidAudienceRequest)
+ .then(() => {
+ return rest.create(config, auth.master(config), '_Audience', iosAudienceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(0);
+ expect(response.count).toEqual(2);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
+ });
+
+ it('should create, read, update and delete audiences throw api', done => {
+ Parse._request(
+ 'POST',
+ 'push_audiences',
+ { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
+ { useMasterKey: true }
+ ).then(() => {
+ Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then(results => {
+ expect(results.results.length).toEqual(1);
+ expect(results.results[0].name).toEqual('My Audience');
+ expect(results.results[0].query.deviceType).toEqual('ios');
+ Parse._request(
+ 'GET',
+ `push_audiences/${results.results[0].objectId}`,
+ {},
+ { useMasterKey: true }
+ ).then(results => {
+ expect(results.name).toEqual('My Audience');
+ expect(results.query.deviceType).toEqual('ios');
+ Parse._request(
+ 'PUT',
+ `push_audiences/${results.objectId}`,
+ { name: 'My Audience 2' },
+ { useMasterKey: true }
+ ).then(() => {
+ Parse._request(
+ 'GET',
+ `push_audiences/${results.objectId}`,
+ {},
+ { useMasterKey: true }
+ ).then(results => {
+ expect(results.name).toEqual('My Audience 2');
+ expect(results.query.deviceType).toEqual('ios');
+ Parse._request(
+ 'DELETE',
+ `push_audiences/${results.objectId}`,
+ {},
+ { useMasterKey: true }
+ ).then(() => {
+ Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then(
+ results => {
+ expect(results.results.length).toEqual(0);
+ done();
+ }
+ );
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ it('should only create with master key', done => {
+ loggerErrorSpy.calls.reset();
+ Parse._request('POST', 'push_audiences', {
+ name: 'My Audience',
+ query: JSON.stringify({ deviceType: 'ios' }),
+ }).then(
+ () => {},
+ error => {
+ expect(error.message).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
+ done();
+ }
+ );
+ });
+
+ it('should only find with master key', done => {
+ loggerErrorSpy.calls.reset();
+ Parse._request('GET', 'push_audiences', {}).then(
+ () => {},
+ error => {
+ expect(error.message).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
+ done();
+ }
+ );
+ });
+
+ it('should only get with master key', done => {
+ loggerErrorSpy.calls.reset();
+ Parse._request('GET', `push_audiences/someId`, {}).then(
+ () => {},
+ error => {
+ expect(error.message).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
+ done();
+ }
+ );
+ });
+
+ it('should only update with master key', done => {
+ loggerErrorSpy.calls.reset();
+ Parse._request('PUT', `push_audiences/someId`, {
+ name: 'My Audience 2',
+ }).then(
+ () => {},
+ error => {
+ expect(error.message).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
+ done();
+ }
+ );
+ });
+
+ it('should only delete with master key', done => {
+ loggerErrorSpy.calls.reset();
+ Parse._request('DELETE', `push_audiences/someId`, {}).then(
+ () => {},
+ error => {
+ expect(error.message).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
+ done();
+ }
+ );
+ });
+
+ it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))('should support legacy parse.com audience fields', done => {
+ const database = Config.get(Parse.applicationId).database.adapter.database;
+ const now = new Date();
+ Parse._request(
+ 'POST',
+ 'push_audiences',
+ { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
+ { useMasterKey: true }
+ ).then(audience => {
+ database
+ .collection('test__Audience')
+ .updateOne(
+ { _id: audience.objectId },
+ {
+ $set: {
+ times_used: 1,
+ _last_used: now,
+ },
+ }
+ )
+ .then(result => {
+ expect(result).toBeTruthy();
+
+ database
+ .collection('test__Audience')
+ .find({ _id: audience.objectId })
+ .toArray()
+ .then(rows => {
+ expect(rows[0]['times_used']).toEqual(1);
+ expect(rows[0]['_last_used']).toEqual(now);
+ Parse._request(
+ 'GET',
+ 'push_audiences/' + audience.objectId,
+ {},
+ { useMasterKey: true }
+ )
+ .then(audience => {
+ expect(audience.name).toEqual('My Audience');
+ expect(audience.query.deviceType).toEqual('ios');
+ expect(audience.timesUsed).toEqual(1);
+ expect(audience.lastUsed).toEqual(now.toISOString());
+ done();
+ })
+ .catch(error => {
+ done.fail(error);
+ });
+ })
+ .catch(error => {
+ done.fail(error);
+ });
+ });
+ });
+ });
+
+ it('should be able to search on audiences', done => {
+ Parse._request(
+ 'POST',
+ 'push_audiences',
+ { name: 'neverUsed', query: JSON.stringify({ deviceType: 'ios' }) },
+ { useMasterKey: true }
+ ).then(() => {
+ const query = {
+ timesUsed: { $exists: false },
+ lastUsed: { $exists: false },
+ };
+ Parse._request(
+ 'GET',
+ 'push_audiences?order=-createdAt&limit=1',
+ { where: query },
+ { useMasterKey: true }
+ )
+ .then(results => {
+ expect(results.results.length).toEqual(1);
+ const audience = results.results[0];
+ expect(audience.name).toEqual('neverUsed');
+ done();
+ })
+ .catch(error => {
+ done.fail(error);
+ });
+ });
+ });
+
+ it('should handle _Audience invalid fields via rest', async () => {
+ await reconfigureServer({
+ appId: 'test',
+ restAPIKey: 'test',
+ masterKey: 'test',
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ try {
+ await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/_Audience',
+ body: { lorem: 'ipsum', _method: 'POST' },
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Master-Key': 'test',
+ 'Content-Type': 'application/json',
+ },
+ });
+ expect(true).toBeFalsy();
+ } catch (e) {
+ expect(e.data.code).toBe(107);
+ expect(e.data.error).toBe('Could not add field lorem');
+ }
+ });
+});
diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js
index 0b19f4ca3a..a055cda5bc 100644
--- a/spec/Auth.spec.js
+++ b/spec/Auth.spec.js
@@ -1,12 +1,13 @@
-describe('Auth', () => {
- var Auth = require('../src/Auth.js').Auth;
+'use strict';
+describe('Auth', () => {
+ const { Auth, getAuthForSessionToken } = require('../lib/Auth.js');
+ const Config = require('../lib/Config');
describe('getUserRoles', () => {
- var auth;
- var config;
- var cacheController;
- var currentRoles = null;
- var currentUserId = 'userId';
+ let auth;
+ let config;
+ let currentRoles = null;
+ const currentUserId = 'userId';
beforeEach(() => {
currentRoles = ['role:userId'];
@@ -15,69 +16,241 @@ describe('Auth', () => {
cacheController: {
role: {
get: () => Promise.resolve(currentRoles),
- set: jasmine.createSpy('set')
- }
- }
- }
+ set: jasmine.createSpy('set'),
+ },
+ },
+ };
spyOn(config.cacheController.role, 'get').and.callThrough();
auth = new Auth({
config: config,
isMaster: false,
user: {
- id: currentUserId
+ id: currentUserId,
},
- installationId: 'installationId'
+ installationId: 'installationId',
});
});
- it('should get user roles from the cache', (done) => {
- auth.getUserRoles()
- .then((roles) => {
- var firstSet = config.cacheController.role.set.calls.first();
- expect(firstSet).toEqual(undefined);
+ it('should get user roles from the cache', done => {
+ auth.getUserRoles().then(roles => {
+ const firstSet = config.cacheController.role.set.calls.first();
+ expect(firstSet).toEqual(undefined);
- var firstGet = config.cacheController.role.get.calls.first();
- expect(firstGet.args[0]).toEqual(currentUserId);
- expect(roles).toEqual(currentRoles);
- done();
- });
+ const firstGet = config.cacheController.role.get.calls.first();
+ expect(firstGet.args[0]).toEqual(currentUserId);
+ expect(roles).toEqual(currentRoles);
+ done();
+ });
});
- it('should only query the roles once', (done) => {
- var loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough();
- auth.getUserRoles()
- .then((roles) => {
+ it('should only query the roles once', done => {
+ const loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough();
+ auth
+ .getUserRoles()
+ .then(roles => {
expect(roles).toEqual(currentRoles);
- return auth.getUserRoles()
+ return auth.getUserRoles();
})
- .then((roles) => auth.getUserRoles())
- .then((roles) => auth.getUserRoles())
- .then((roles) => {
+ .then(() => auth.getUserRoles())
+ .then(() => auth.getUserRoles())
+ .then(roles => {
// Should only call the cache adapter once.
expect(config.cacheController.role.get.calls.count()).toEqual(1);
expect(loadRolesSpy.calls.count()).toEqual(1);
- var firstGet = config.cacheController.role.get.calls.first();
+ const firstGet = config.cacheController.role.get.calls.first();
expect(firstGet.args[0]).toEqual(currentUserId);
expect(roles).toEqual(currentRoles);
done();
});
});
- it('should not have any roles with no user', (done) => {
- auth.user = null
- auth.getUserRoles()
- .then((roles) => expect(roles).toEqual([]))
+ it('should not have any roles with no user', done => {
+ auth.user = null;
+ auth
+ .getUserRoles()
+ .then(roles => expect(roles).toEqual([]))
.then(() => done());
});
- it('should not have any user roles with master', (done) => {
- auth.isMaster = true
- auth.getUserRoles()
- .then((roles) => expect(roles).toEqual([]))
+ it('should not have any user roles with master', done => {
+ auth.isMaster = true;
+ auth
+ .getUserRoles()
+ .then(roles => expect(roles).toEqual([]))
.then(() => done());
- })
+ });
+ });
+
+ it('can use extendSessionOnUse', async () => {
+ await reconfigureServer({
+ extendSessionOnUse: true,
+ });
+
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ const session = await new Parse.Query(Parse.Session).first();
+ const updatedAt = new Date('2010');
+ const expiry = new Date();
+ expiry.setHours(expiry.getHours() + 1);
+
+ await Parse.Server.database.update(
+ '_Session',
+ { objectId: session.id },
+ {
+ expiresAt: { __type: 'Date', iso: expiry.toISOString() },
+ updatedAt: updatedAt.toISOString(),
+ }
+ );
+ Parse.Server.cacheController.clear();
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ await session.fetch();
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ await session.fetch();
+ expect(session.get('expiresAt') > expiry).toBeTrue();
+ });
+
+ it('should load auth without a config', async () => {
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ expect(user.getSessionToken()).not.toBeUndefined();
+ const userAuth = await getAuthForSessionToken({
+ sessionToken: user.getSessionToken(),
+ });
+ expect(userAuth.user instanceof Parse.User).toBe(true);
+ expect(userAuth.user.id).toBe(user.id);
+ });
+
+ it('should load auth with a config', async () => {
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ expect(user.getSessionToken()).not.toBeUndefined();
+ const userAuth = await getAuthForSessionToken({
+ sessionToken: user.getSessionToken(),
+ config: Config.get('test'),
+ });
+ expect(userAuth.user instanceof Parse.User).toBe(true);
+ expect(userAuth.user.id).toBe(user.id);
+ });
+
+ describe('getRolesForUser', () => {
+ const rolesNumber = 100;
+
+ it('should load all roles without config', async () => {
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ expect(user.getSessionToken()).not.toBeUndefined();
+ const userAuth = await getAuthForSessionToken({
+ sessionToken: user.getSessionToken(),
+ });
+ const roles = [];
+ for (let i = 0; i < rolesNumber; i++) {
+ const acl = new Parse.ACL();
+ const role = new Parse.Role('roleloadtest' + i, acl);
+ role.getUsers().add([user]);
+ roles.push(role);
+ }
+ const savedRoles = await Parse.Object.saveAll(roles);
+ expect(savedRoles.length).toBe(rolesNumber);
+ const cloudRoles = await userAuth.getRolesForUser();
+ expect(cloudRoles.length).toBe(rolesNumber);
+ });
+
+ it('should load all roles with config', async () => {
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ expect(user.getSessionToken()).not.toBeUndefined();
+ const userAuth = await getAuthForSessionToken({
+ sessionToken: user.getSessionToken(),
+ config: Config.get('test'),
+ });
+ const roles = [];
+ for (let i = 0; i < rolesNumber; i++) {
+ const acl = new Parse.ACL();
+ const role = new Parse.Role('roleloadtest' + i, acl);
+ role.getUsers().add([user]);
+ roles.push(role);
+ }
+ const savedRoles = await Parse.Object.saveAll(roles);
+ expect(savedRoles.length).toBe(rolesNumber);
+ const cloudRoles = await userAuth.getRolesForUser();
+ expect(cloudRoles.length).toBe(rolesNumber);
+ });
+
+ it('should load all roles for different users with config', async () => {
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'hello',
+ password: 'password',
+ });
+ const user2 = new Parse.User();
+ await user2.signUp({
+ username: 'world',
+ password: '1234',
+ });
+ expect(user.getSessionToken()).not.toBeUndefined();
+ const userAuth = await getAuthForSessionToken({
+ sessionToken: user.getSessionToken(),
+ config: Config.get('test'),
+ });
+ const user2Auth = await getAuthForSessionToken({
+ sessionToken: user2.getSessionToken(),
+ config: Config.get('test'),
+ });
+ const roles = [];
+ for (let i = 0; i < rolesNumber; i += 1) {
+ const acl = new Parse.ACL();
+ const acl2 = new Parse.ACL();
+ const role = new Parse.Role('roleloadtest' + i, acl);
+ const role2 = new Parse.Role('role2loadtest' + i, acl2);
+ role.getUsers().add([user]);
+ role2.getUsers().add([user2]);
+ roles.push(role);
+ roles.push(role2);
+ }
+ const savedRoles = await Parse.Object.saveAll(roles);
+ expect(savedRoles.length).toBe(rolesNumber * 2);
+ const cloudRoles = await userAuth.getRolesForUser();
+ const cloudRoles2 = await user2Auth.getRolesForUser();
+ expect(cloudRoles.length).toBe(rolesNumber);
+ expect(cloudRoles2.length).toBe(rolesNumber);
+ });
+ });
+});
+
+describe('extendSessionOnUse', () => {
+ it(`shouldUpdateSessionExpiry()`, async () => {
+ const { shouldUpdateSessionExpiry } = require('../lib/Auth');
+ let update = new Date(Date.now() - 86410 * 1000);
+
+ const res = shouldUpdateSessionExpiry(
+ { sessionLength: 86460 },
+ { updatedAt: update }
+ );
+
+ update = new Date(Date.now() - 43210 * 1000);
+ const res2 = shouldUpdateSessionExpiry(
+ { sessionLength: 86460 },
+ { updatedAt: update }
+ );
+ expect(res).toBe(true);
+ expect(res2).toBe(false);
});
});
diff --git a/spec/AuthDataUniqueIndex.spec.js b/spec/AuthDataUniqueIndex.spec.js
new file mode 100644
index 0000000000..d975a42209
--- /dev/null
+++ b/spec/AuthDataUniqueIndex.spec.js
@@ -0,0 +1,210 @@
+'use strict';
+
+const request = require('../lib/request');
+const Config = require('../lib/Config');
+
+describe('AuthData Unique Index', () => {
+ const fakeAuthProvider = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+
+ beforeEach(async () => {
+ await reconfigureServer({ auth: { fakeAuthProvider } });
+ });
+
+ it('should prevent concurrent signups with the same authData from creating duplicate users', async () => {
+ const authData = { fakeAuthProvider: { id: 'duplicate-test-id', token: 'token1' } };
+
+ // Fire multiple concurrent signup requests with the same authData
+ const concurrentRequests = Array.from({ length: 5 }, () =>
+ request({
+ method: 'POST',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ url: 'http://localhost:8378/1/users',
+ body: { authData },
+ }).then(
+ response => ({ success: true, data: response.data }),
+ error => ({ success: false, error: error.data || error.message })
+ )
+ );
+
+ const results = await Promise.all(concurrentRequests);
+ const successes = results.filter(r => r.success);
+ const failures = results.filter(r => !r.success);
+
+ // All should either succeed (returning the same user) or fail with "this auth is already used"
+ // The key invariant: only ONE unique objectId should exist
+ const uniqueObjectIds = new Set(successes.map(r => r.data.objectId));
+ expect(uniqueObjectIds.size).toBe(1);
+
+ // Failures should be "this auth is already used" errors
+ for (const failure of failures) {
+ expect(failure.error.code).toBe(208);
+ expect(failure.error.error).toBe('this auth is already used');
+ }
+
+ // Verify only one user exists in the database with this authData
+ const query = new Parse.Query('_User');
+ query.equalTo('authData.fakeAuthProvider.id', 'duplicate-test-id');
+ const users = await query.find({ useMasterKey: true });
+ expect(users.length).toBe(1);
+ });
+
+ it('should prevent concurrent signups via batch endpoint with same authData', async () => {
+ const authData = { fakeAuthProvider: { id: 'batch-race-test-id', token: 'token1' } };
+
+ const response = await request({
+ method: 'POST',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ url: 'http://localhost:8378/1/batch',
+ body: {
+ requests: Array.from({ length: 3 }, () => ({
+ method: 'POST',
+ path: '/1/users',
+ body: { authData },
+ })),
+ },
+ });
+
+ const results = response.data;
+ const successes = results.filter(r => r.success);
+ const failures = results.filter(r => r.error);
+
+ // All successes should reference the same user
+ const uniqueObjectIds = new Set(successes.map(r => r.success.objectId));
+ expect(uniqueObjectIds.size).toBe(1);
+
+ // Failures should be "this auth is already used" errors
+ for (const failure of failures) {
+ expect(failure.error.code).toBe(208);
+ expect(failure.error.error).toBe('this auth is already used');
+ }
+
+ // Verify only one user exists in the database with this authData
+ const query = new Parse.Query('_User');
+ query.equalTo('authData.fakeAuthProvider.id', 'batch-race-test-id');
+ const users = await query.find({ useMasterKey: true });
+ expect(users.length).toBe(1);
+ });
+
+ it('should allow sequential signups with different authData IDs', async () => {
+ const user1 = await Parse.User.logInWith('fakeAuthProvider', {
+ authData: { id: 'user-id-1', token: 'token1' },
+ });
+ const user2 = await Parse.User.logInWith('fakeAuthProvider', {
+ authData: { id: 'user-id-2', token: 'token2' },
+ });
+
+ expect(user1.id).toBeDefined();
+ expect(user2.id).toBeDefined();
+ expect(user1.id).not.toBe(user2.id);
+ });
+
+ it('should still allow login with authData after successful signup', async () => {
+ const authPayload = { authData: { id: 'login-test-id', token: 'token1' } };
+
+ // Signup
+ const user1 = await Parse.User.logInWith('fakeAuthProvider', authPayload);
+ expect(user1.id).toBeDefined();
+
+ // Login again with same authData â should return same user
+ const user2 = await Parse.User.logInWith('fakeAuthProvider', authPayload);
+ expect(user2.id).toBe(user1.id);
+ });
+
+ it('should skip startup index creation when createIndexAuthDataUniqueness is false', async () => {
+ const config = Config.get('test');
+ const adapter = config.database.adapter;
+ const spy = spyOn(adapter, 'ensureAuthDataUniqueness').and.callThrough();
+
+ // Temporarily set the option to false
+ const originalOptions = config.database.options.databaseOptions;
+ config.database.options.databaseOptions = { createIndexAuthDataUniqueness: false };
+
+ await config.database.performInitialization();
+ expect(spy).not.toHaveBeenCalled();
+
+ // Restore original options
+ config.database.options.databaseOptions = originalOptions;
+ });
+
+ it('should handle calling ensureAuthDataUniqueness multiple times (idempotent)', async () => {
+ const config = Config.get('test');
+ const adapter = config.database.adapter;
+
+ // Both calls should succeed (index creation is idempotent)
+ await adapter.ensureAuthDataUniqueness('fakeAuthProvider');
+ await adapter.ensureAuthDataUniqueness('fakeAuthProvider');
+ });
+
+ it('should log warning when index creation fails due to existing duplicates', async () => {
+ const config = Config.get('test');
+ const adapter = config.database.adapter;
+
+ // Spy on the adapter to simulate a duplicate value error
+ spyOn(adapter, 'ensureAuthDataUniqueness').and.callFake(() => {
+ return Promise.reject(
+ new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'duplicates exist')
+ );
+ });
+
+ const logSpy = spyOn(require('../lib/logger').logger, 'warn');
+
+ // Re-run performInitialization â should warn but not throw
+ await config.database.performInitialization();
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.stringContaining('Unable to ensure uniqueness for auth data provider'),
+ jasmine.anything()
+ );
+ });
+
+ it('should prevent concurrent signups with same anonymous authData', async () => {
+ const anonymousId = 'anon-race-test-id';
+ const authData = { anonymous: { id: anonymousId } };
+
+ const concurrentRequests = Array.from({ length: 5 }, () =>
+ request({
+ method: 'POST',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ url: 'http://localhost:8378/1/users',
+ body: { authData },
+ }).then(
+ response => ({ success: true, data: response.data }),
+ error => ({ success: false, error: error.data || error.message })
+ )
+ );
+
+ const results = await Promise.all(concurrentRequests);
+ const successes = results.filter(r => r.success);
+ const failures = results.filter(r => !r.success);
+
+ // All successes should reference the same user
+ const uniqueObjectIds = new Set(successes.map(r => r.data.objectId));
+ expect(uniqueObjectIds.size).toBe(1);
+
+ // Failures should be "this auth is already used" errors
+ for (const failure of failures) {
+ expect(failure.error.code).toBe(208);
+ expect(failure.error.error).toBe('this auth is already used');
+ }
+
+ // Verify only one user exists in the database with this authData
+ const query = new Parse.Query('_User');
+ query.equalTo('authData.anonymous.id', anonymousId);
+ const users = await query.find({ useMasterKey: true });
+ expect(users.length).toBe(1);
+ });
+});
diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js
new file mode 100644
index 0000000000..f975914bd7
--- /dev/null
+++ b/spec/AuthenticationAdapters.spec.js
@@ -0,0 +1,2222 @@
+const request = require('../lib/request');
+const Config = require('../lib/Config');
+const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns;
+const authenticationLoader = require('../lib/Adapters/Auth');
+const path = require('path');
+
+describe('AuthenticationProviders', function () {
+ const getMockMyOauthProvider = function () {
+ return {
+ authData: {
+ id: '12345',
+ access_token: '12345',
+ expiration_date: new Date().toJSON(),
+ },
+ shouldError: false,
+ loggedOut: false,
+ synchronizedUserId: null,
+ synchronizedAuthToken: null,
+ synchronizedExpiration: null,
+
+ authenticate: function (options) {
+ if (this.shouldError) {
+ options.error(this, 'An error occurred');
+ } else if (this.shouldCancel) {
+ options.error(this, null);
+ } else {
+ options.success(this, this.authData);
+ }
+ },
+ restoreAuthentication: function (authData) {
+ if (!authData) {
+ this.synchronizedUserId = null;
+ this.synchronizedAuthToken = null;
+ this.synchronizedExpiration = null;
+ return true;
+ }
+ this.synchronizedUserId = authData.id;
+ this.synchronizedAuthToken = authData.access_token;
+ this.synchronizedExpiration = authData.expiration_date;
+ return true;
+ },
+ getAuthType: function () {
+ return 'myoauth';
+ },
+ deauthenticate: function () {
+ this.loggedOut = true;
+ this.restoreAuthentication(null);
+ },
+ };
+ };
+
+ Parse.User.extend({
+ extended: function () {
+ return true;
+ },
+ });
+
+ const createOAuthUser = function (callback) {
+ return createOAuthUserWithSessionToken(undefined, callback);
+ };
+
+ const createOAuthUserWithSessionToken = function (token, callback) {
+ const jsonBody = {
+ authData: {
+ myoauth: getMockMyOauthProvider().authData,
+ },
+ };
+
+ const options = {
+ method: 'POST',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Installation-Id': 'yolo',
+ 'X-Parse-Session-Token': token,
+ 'Content-Type': 'application/json',
+ },
+ url: 'http://localhost:8378/1/users',
+ body: jsonBody,
+ };
+ return request(options)
+ .then(response => {
+ if (callback) {
+ callback(null, response, response.data);
+ }
+ return {
+ res: response,
+ body: response.data,
+ };
+ })
+ .catch(error => {
+ if (callback) {
+ callback(error);
+ }
+ throw error;
+ });
+ };
+
+ it('should create user with REST API', done => {
+ createOAuthUser((error, response, body) => {
+ expect(error).toBe(null);
+ const b = body;
+ ok(b.sessionToken);
+ expect(b.objectId).not.toBeNull();
+ expect(b.objectId).not.toBeUndefined();
+ const sessionToken = b.sessionToken;
+ const q = new Parse.Query('_Session');
+ q.equalTo('sessionToken', sessionToken);
+ q.first({ useMasterKey: true })
+ .then(res => {
+ if (!res) {
+ fail('should not fail fetching the session');
+ done();
+ return;
+ }
+ expect(res.get('installationId')).toEqual('yolo');
+ done();
+ })
+ .catch(() => {
+ fail('should not fail fetching the session');
+ done();
+ });
+ });
+ });
+
+ it('should only create a single user with REST API', done => {
+ let objectId;
+ createOAuthUser((error, response, body) => {
+ expect(error).toBe(null);
+ const b = body;
+ expect(b.objectId).not.toBeNull();
+ expect(b.objectId).not.toBeUndefined();
+ objectId = b.objectId;
+
+ createOAuthUser((error, response, body) => {
+ expect(error).toBe(null);
+ const b = body;
+ expect(b.objectId).not.toBeNull();
+ expect(b.objectId).not.toBeUndefined();
+ expect(b.objectId).toBe(objectId);
+ done();
+ });
+ });
+ });
+
+ it("should fail to link if session token don't match user", done => {
+ Parse.User.signUp('myUser', 'password')
+ .then(user => {
+ return createOAuthUserWithSessionToken(user.getSessionToken());
+ })
+ .then(() => {
+ return Parse.User.logOut();
+ })
+ .then(() => {
+ return Parse.User.signUp('myUser2', 'password');
+ })
+ .then(user => {
+ return createOAuthUserWithSessionToken(user.getSessionToken());
+ })
+ .then(fail, ({ data }) => {
+ expect(data.code).toBe(208);
+ expect(data.error).toBe('this auth is already used');
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('should support loginWith with session token and with/without mutated authData', async () => {
+ const fakeAuthProvider = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+ const payload = { authData: { id: 'user1', token: 'fakeToken' } };
+ const payload2 = { authData: { id: 'user1', token: 'fakeToken2' } };
+ await reconfigureServer({ auth: { fakeAuthProvider } });
+ const user = await Parse.User.logInWith('fakeAuthProvider', payload);
+ const user2 = await Parse.User.logInWith('fakeAuthProvider', payload, {
+ sessionToken: user.getSessionToken(),
+ });
+ const user3 = await Parse.User.logInWith('fakeAuthProvider', payload2, {
+ sessionToken: user2.getSessionToken(),
+ });
+ expect(user.id).toEqual(user2.id);
+ expect(user.id).toEqual(user3.id);
+ });
+
+ it('should support sync/async validateAppId', async () => {
+ const syncProvider = {
+ validateAppId: () => true,
+ appIds: 'test',
+ validateAuthData: () => Promise.resolve(),
+ };
+ const asyncProvider = {
+ appIds: 'test',
+ validateAppId: () => Promise.resolve(true),
+ validateAuthData: () => Promise.resolve(),
+ };
+ const payload = { authData: { id: 'user1', token: 'fakeToken' } };
+ const syncSpy = spyOn(syncProvider, 'validateAppId');
+ const asyncSpy = spyOn(asyncProvider, 'validateAppId');
+
+ await reconfigureServer({ auth: { asyncProvider, syncProvider } });
+ const user = await Parse.User.logInWith('asyncProvider', payload);
+ const user2 = await Parse.User.logInWith('syncProvider', payload);
+ expect(user.getSessionToken()).toBeDefined();
+ expect(user2.getSessionToken()).toBeDefined();
+ expect(syncSpy).toHaveBeenCalledTimes(1);
+ expect(asyncSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('unlink and link with custom provider', async () => {
+ const provider = getMockMyOauthProvider();
+ Parse.User._registerAuthenticationProvider(provider);
+ const model = await Parse.User._logInWith('myoauth');
+ ok(model instanceof Parse.User, 'Model should be a Parse.User');
+ strictEqual(Parse.User.current(), model);
+ ok(model.extended(), 'Should have used the subclass.');
+ strictEqual(provider.authData.id, provider.synchronizedUserId);
+ strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
+ strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
+ ok(model._isLinked('myoauth'), 'User should be linked to myoauth');
+
+ await model._unlinkFrom('myoauth');
+ ok(!model._isLinked('myoauth'), 'User should not be linked to myoauth');
+ ok(!provider.synchronizedUserId, 'User id should be cleared');
+ ok(!provider.synchronizedAuthToken, 'Auth token should be cleared');
+ ok(!provider.synchronizedExpiration, 'Expiration should be cleared');
+ // make sure the auth data is properly deleted
+ const config = Config.get(Parse.applicationId);
+ const res = await config.database.adapter.find(
+ '_User',
+ {
+ fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation),
+ },
+ { objectId: model.id },
+ {}
+ );
+ expect(res.length).toBe(1);
+ expect(res[0]._auth_data_myoauth).toBeUndefined();
+ expect(res[0]._auth_data_myoauth).not.toBeNull();
+
+ await model._linkWith('myoauth');
+
+ ok(provider.synchronizedUserId, 'User id should have a value');
+ ok(provider.synchronizedAuthToken, 'Auth token should have a value');
+ ok(provider.synchronizedExpiration, 'Expiration should have a value');
+ ok(model._isLinked('myoauth'), 'User should be linked to myoauth');
+ });
+
+ function validateValidator(validator) {
+ expect(typeof validator).toBe('function');
+ }
+
+ function validateAuthenticationHandler(authenticationHandler) {
+ expect(authenticationHandler).not.toBeUndefined();
+ expect(typeof authenticationHandler.getValidatorForProvider).toBe('function');
+ expect(typeof authenticationHandler.getValidatorForProvider).toBe('function');
+ }
+
+ function validateAuthenticationAdapter(authAdapter) {
+ expect(authAdapter).not.toBeUndefined();
+ if (!authAdapter) {
+ return;
+ }
+ expect(typeof authAdapter.validateAuthData).toBe('function');
+ expect(typeof authAdapter.validateAppId).toBe('function');
+ }
+
+ it('properly loads custom adapter', done => {
+ const validAuthData = {
+ id: 'hello',
+ token: 'world',
+ };
+ const adapter = {
+ validateAppId: function () {
+ return Promise.resolve();
+ },
+ validateAuthData: function (authData) {
+ if (authData.id == validAuthData.id && authData.token == validAuthData.token) {
+ return Promise.resolve();
+ }
+ return Promise.reject();
+ },
+ };
+
+ const authDataSpy = spyOn(adapter, 'validateAuthData').and.callThrough();
+ const appIdSpy = spyOn(adapter, 'validateAppId').and.callThrough();
+
+ const authenticationHandler = authenticationLoader({
+ customAuthentication: adapter,
+ });
+
+ validateAuthenticationHandler(authenticationHandler);
+ const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication');
+ validateValidator(validator);
+
+ validator(validAuthData, {}, {}).then(
+ () => {
+ expect(authDataSpy).toHaveBeenCalled();
+ // AppIds are not provided in the adapter, should not be called
+ expect(appIdSpy).not.toHaveBeenCalled();
+ done();
+ },
+ err => {
+ jfail(err);
+ done();
+ }
+ );
+ });
+
+ it('properly loads custom adapter module object', done => {
+ const authenticationHandler = authenticationLoader({
+ customAuthentication: path.resolve('./spec/support/CustomAuth.js'),
+ });
+
+ validateAuthenticationHandler(authenticationHandler);
+ const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication');
+ validateValidator(validator);
+ validator(
+ {
+ token: 'my-token',
+ },
+ {},
+ {}
+ ).then(
+ () => {
+ done();
+ },
+ err => {
+ jfail(err);
+ done();
+ }
+ );
+ });
+
+ it('properly loads custom adapter module object (again)', done => {
+ const authenticationHandler = authenticationLoader({
+ customAuthentication: {
+ module: path.resolve('./spec/support/CustomAuthFunction.js'),
+ options: { token: 'valid-token' },
+ },
+ });
+
+ validateAuthenticationHandler(authenticationHandler);
+ const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication');
+ validateValidator(validator);
+
+ validator(
+ {
+ token: 'valid-token',
+ },
+ {},
+ {}
+ ).then(
+ () => {
+ done();
+ },
+ err => {
+ jfail(err);
+ done();
+ }
+ );
+ });
+
+ it('properly loads a default adapter with options', () => {
+ const options = {
+ facebook: {
+ appIds: ['a', 'b'],
+ appSecret: 'secret',
+ },
+ };
+ const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
+ 'facebook',
+ options
+ );
+ validateAuthenticationAdapter(adapter);
+ expect(appIds).toEqual(['a', 'b']);
+ expect(providerOptions).toEqual(options.facebook);
+ });
+
+ it('should handle Facebook appSecret for validating appIds', async () => {
+ const httpsRequest = require('../lib/Adapters/Auth/httpsRequest');
+ spyOn(httpsRequest, 'get').and.callFake(() => {
+ return Promise.resolve({ id: 'a' });
+ });
+ const options = {
+ facebook: {
+ appIds: ['a', 'b'],
+ appSecret: 'secret_sauce',
+ },
+ };
+ const authData = {
+ access_token: 'badtoken',
+ };
+ const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
+ 'facebook',
+ options
+ );
+ await adapter.validateAppId(appIds, authData, providerOptions);
+ expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true);
+ });
+
+ it('should throw error when Facebook request appId is wrong data type', async () => {
+ const httpsRequest = require('../lib/Adapters/Auth/httpsRequest');
+ spyOn(httpsRequest, 'get').and.callFake(() => {
+ return Promise.resolve({ id: 'a' });
+ });
+ const options = {
+ facebook: {
+ appIds: 'abcd',
+ appSecret: 'secret_sauce',
+ },
+ };
+ const authData = {
+ access_token: 'badtoken',
+ };
+ const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
+ 'facebook',
+ options
+ );
+ await expectAsync(adapter.validateAppId(appIds, authData, providerOptions)).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.')
+ );
+ });
+
+ it('should handle Facebook appSecret for validating auth data', async () => {
+ const httpsRequest = require('../lib/Adapters/Auth/httpsRequest');
+ spyOn(httpsRequest, 'get').and.callFake(() => {
+ return Promise.resolve();
+ });
+ const options = {
+ facebook: {
+ appIds: ['a', 'b'],
+ appSecret: 'secret_sauce',
+ },
+ };
+ const authData = {
+ id: 'test',
+ access_token: 'test',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('facebook', options);
+ await adapter.validateAuthData(authData, providerOptions);
+ expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true);
+ });
+
+ it('properly loads a custom adapter with options', () => {
+ const options = {
+ custom: {
+ validateAppId: () => {},
+ validateAuthData: () => {},
+ appIds: ['a', 'b'],
+ },
+ };
+ const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter(
+ 'custom',
+ options
+ );
+ validateAuthenticationAdapter(adapter);
+ expect(appIds).toEqual(['a', 'b']);
+ expect(providerOptions).toEqual(options.custom);
+ });
+
+ it('can disable provider', async () => {
+ await reconfigureServer({
+ auth: {
+ myoauth: {
+ enabled: false,
+ module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src
+ },
+ },
+ });
+ const provider = getMockMyOauthProvider();
+ Parse.User._registerAuthenticationProvider(provider);
+ await expectAsync(Parse.User._logInWith('myoauth')).toBeRejectedWith(
+ new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, 'This authentication method is unsupported.')
+ );
+ });
+});
+
+describe('google auth adapter', () => {
+ const google = require('../lib/Adapters/Auth/google');
+ const jwt = require('jsonwebtoken');
+ const authUtils = require('../lib/Adapters/Auth/utils');
+
+ it('should throw error with missing id_token', async () => {
+ try {
+ await google.validateAuthData({}, { clientId: 'secret' });
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('id token is invalid for this user.');
+ }
+ });
+
+ it('should not decode invalid id_token', async () => {
+ try {
+ await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, { clientId: 'secret' });
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('provided token does not decode as JWT');
+ }
+ });
+
+ it('should reject forged alg:none JWT from advisory PoC (GHSA-4q3h-vp4r-prv2)', async () => {
+ const header = Buffer.from('{"alg":"none","kid":"nonexistent-key","typ":"JWT"}').toString('base64url');
+ const payload = Buffer.from('{"sub":"the_user_id","iss":"accounts.google.com","aud":"secret","exp":9999999999}').toString('base64url');
+ const forgedToken = `${header}.${payload}.`;
+
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+
+ try {
+ await google.validateAuthData(
+ { id: 'the_user_id', id_token: forgedToken },
+ { clientId: 'secret' }
+ );
+ fail('should have rejected forged token');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ });
+
+ it('should pass hardcoded RS256 algorithm to jwt.verify, not the JWT header alg', async () => {
+ const fakeClaim = {
+ iss: 'https://accounts.google.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'ES256' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ await google.validateAuthData(
+ { id: 'the_user_id', id_token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('should throw error if Google signing key is not found', async () => {
+ const fakeDecodedToken = { kid: '789', alg: 'RS256' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.rejectWith(new Error('key not found'));
+
+ try {
+ await google.validateAuthData(
+ { id: 'the_user_id', id_token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Unable to find matching key for Key ID: 789');
+ }
+ });
+
+ it('(using client id as string) should verify id_token (google.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://accounts.google.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'RS256' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await google.validateAuthData(
+ { id: 'the_user_id', id_token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://not.google.com',
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'RS256' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await google.validateAuthData(
+ { id: 'the_user_id', id_token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com'
+ );
+ }
+ });
+
+ xit('(using client id as string) should throw error with invalid jwt client_id', async () => {
+ const fakeClaim = {
+ iss: 'https://accounts.google.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'RS256' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await google.validateAuthData(
+ { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt audience invalid. expected: secret');
+ }
+ });
+
+ xit('should throw error with invalid user id', async () => {
+ const fakeClaim = {
+ iss: 'https://accounts.google.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'RS256' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await google.validateAuthData(
+ { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' },
+ { clientId: 'INSERT CLIENT ID HERE' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it('should throw error when clientId is not configured', async () => {
+ try {
+ await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, {});
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Google auth is not configured.');
+ }
+ });
+});
+
+describe('keycloak auth adapter', () => {
+ const keycloak = require('../lib/Adapters/Auth/keycloak');
+ const jwt = require('jsonwebtoken');
+ const authUtils = require('../lib/Adapters/Auth/utils');
+
+ it('validateAuthData should fail without access token', async () => {
+ const authData = {
+ id: 'fakeid',
+ };
+ try {
+ await keycloak.validateAuthData(authData);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Missing access token and/or User id');
+ }
+ });
+
+ it('validateAuthData should fail without user id', async () => {
+ const authData = {
+ access_token: 'sometoken',
+ };
+ try {
+ await keycloak.validateAuthData(authData);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Missing access token and/or User id');
+ }
+ });
+
+ it('validateAuthData should fail without config', async () => {
+ const options = {
+ keycloak: {
+ config: null,
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'sometoken',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Missing keycloak configuration');
+ }
+ });
+
+ it('validateAuthData should fail without client-id', async () => {
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'sometoken',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Keycloak auth is not configured. Missing client-id.');
+ }
+ });
+
+ it('validateAuthData should fail with invalid JWT token', async () => {
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'not-a-jwt',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('provided token does not decode as JWT');
+ }
+ });
+
+ it('validateAuthData should fail with wrong issuer', async () => {
+ const fakeClaim = {
+ iss: 'https://evil.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'access token not issued by correct provider - expected: https://auth.example.com/realms/my-realm | from: https://evil.example.com/realms/my-realm'
+ );
+ }
+ });
+
+ it('validateAuthData should fail with wrong azp (audience)', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'other-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'access token is not authorized for this client - expected: parse-app | from: other-app'
+ );
+ }
+ });
+
+ it('validateAuthData should fail with wrong sub', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'wrong-id',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it('validateAuthData should fail with invalid roles (JWT validation)', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ roles: ['role1'],
+ groups: ['group1'],
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ roles: ['wrong-role'],
+ groups: ['group1'],
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Invalid authentication');
+ }
+ });
+
+ it('validateAuthData should fail with invalid groups (JWT validation)', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ roles: ['role1'],
+ groups: ['group1'],
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ roles: ['role1'],
+ groups: ['wrong-group'],
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Invalid authentication');
+ }
+ });
+
+ it('validateAuthData should handle successful authentication', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ roles: ['role1'],
+ groups: ['group1'],
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ roles: ['role1'],
+ groups: ['group1'],
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ await adapter.validateAuthData(authData, providerOptions);
+ expect(jwt.verify).toHaveBeenCalled();
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('validateAuthData should handle successful authentication without roles and groups', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ await adapter.validateAuthData(authData, providerOptions);
+ expect(jwt.verify).toHaveBeenCalled();
+ });
+
+ it('validateAuthData should use hardcoded RS256 algorithm, not JWT header alg', async () => {
+ const fakeClaim = {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'fakeid',
+ exp: Math.floor(Date.now() / 1000) + 3600,
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'none' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'fakeid',
+ access_token: 'fake.jwt.token',
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ await adapter.validateAuthData(authData, providerOptions);
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('validateAuthData should verify a real signed JWT end-to-end', async () => {
+ const crypto = require('crypto');
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
+ modulusLength: 2048,
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
+ });
+
+ const token = jwt.sign(
+ {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'user123',
+ roles: ['admin'],
+ groups: ['staff'],
+ },
+ privateKey,
+ { algorithm: 'RS256', keyid: 'test-key-1', expiresIn: '1h' }
+ );
+
+ // Only mock the JWKS key fetch â jwt.verify runs for real
+ spyOn(authUtils, 'getSigningKey').and.resolveTo({
+ kid: 'test-key-1',
+ publicKey: publicKey,
+ });
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'user123',
+ access_token: token,
+ roles: ['admin'],
+ groups: ['staff'],
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ const result = await adapter.validateAuthData(authData, providerOptions);
+ expect(result.sub).toBe('user123');
+ expect(result.azp).toBe('parse-app');
+ expect(result.iss).toBe('https://auth.example.com/realms/my-realm');
+ });
+
+ it('validateAuthData should reject a JWT signed with a different key', async () => {
+ const crypto = require('crypto');
+ const { privateKey } = crypto.generateKeyPairSync('rsa', {
+ modulusLength: 2048,
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
+ });
+ const { publicKey: differentPublicKey } = crypto.generateKeyPairSync('rsa', {
+ modulusLength: 2048,
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
+ });
+
+ const token = jwt.sign(
+ {
+ iss: 'https://auth.example.com/realms/my-realm',
+ azp: 'parse-app',
+ sub: 'user123',
+ },
+ privateKey,
+ { algorithm: 'RS256', keyid: 'test-key-1', expiresIn: '1h' }
+ );
+
+ // Return a different public key â signature verification should fail
+ spyOn(authUtils, 'getSigningKey').and.resolveTo({
+ kid: 'test-key-1',
+ publicKey: differentPublicKey,
+ });
+
+ const options = {
+ keycloak: {
+ config: {
+ 'auth-server-url': 'https://auth.example.com',
+ realm: 'my-realm',
+ 'client-id': 'parse-app',
+ },
+ },
+ };
+ const authData = {
+ id: 'user123',
+ access_token: token,
+ };
+ const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options);
+ try {
+ await adapter.validateAuthData(authData, providerOptions);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('invalid signature');
+ }
+ });
+});
+
+describe('apple signin auth adapter', () => {
+ const apple = require('../lib/Adapters/Auth/apple');
+ const jwt = require('jsonwebtoken');
+ const authUtils = require('../lib/Adapters/Auth/utils');
+
+ it('(using client id as string) should throw error with missing id_token', async () => {
+ try {
+ await apple.validateAuthData({}, { clientId: 'secret' });
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('id token is invalid for this user.');
+ }
+ });
+
+ it('(using client id as array) should throw error with missing id_token', async () => {
+ try {
+ await apple.validateAuthData({}, { clientId: ['secret'] });
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('id token is invalid for this user.');
+ }
+ });
+
+ it('should not decode invalid id_token', async () => {
+ try {
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('provided token does not decode as JWT');
+ }
+ });
+
+ it('should throw error if public key used to encode token is not available', async () => {
+ const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } };
+ try {
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ `Unable to find matching key for Key ID: ${fakeDecodedToken.header.kid}`
+ );
+ }
+ });
+
+ it('should use algorithm from key header to verify id_token (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ expect(result).toEqual(fakeClaim);
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('should pass hardcoded RS256 algorithm to jwt.verify, not the JWT header alg (GHSA-4q3h-vp4r-prv2)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'none' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('should not verify invalid id_token', async () => {
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+
+ try {
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt malformed');
+ }
+ });
+
+ it('(using client id as array) should not verify invalid id_token', async () => {
+ try {
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('provided token does not decode as JWT');
+ }
+ });
+
+ it('(using client id as string) should verify id_token (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it('(using client id as array) should verify id_token (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: ['secret'] }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it('(using client id as array with multiple items) should verify id_token (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: ['secret', 'secret 123'] }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it('(using client id as string) should throw error with with invalid jwt issuer (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://not.apple.com',
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
+ );
+ }
+ });
+
+ // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
+ // and a private key
+ xit('(using client id as array) should throw error with with invalid jwt issuer', async () => {
+ const fakeClaim = {
+ iss: 'https://not.apple.com',
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await apple.validateAuthData(
+ {
+ id: 'INSERT ID HERE',
+ token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER',
+ },
+ { clientId: ['INSERT CLIENT ID HERE'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
+ );
+ }
+ });
+
+ it('(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://not.apple.com',
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await apple.validateAuthData(
+ {
+ id: 'INSERT ID HERE',
+ token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER',
+ },
+ { clientId: 'INSERT CLIENT ID HERE' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
+ );
+ }
+ });
+
+ // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
+ // and a private key
+ xit('(using client id as string) should throw error with invalid jwt clientId', async () => {
+ try {
+ await apple.validateAuthData(
+ { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt audience invalid. expected: secret');
+ }
+ });
+
+ // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
+ // and a private key
+ xit('(using client id as array) should throw error with invalid jwt clientId', async () => {
+ try {
+ await apple.validateAuthData(
+ { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' },
+ { clientId: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt audience invalid. expected: secret');
+ }
+ });
+
+ // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
+ // and a private key
+ xit('should throw error with invalid user id', async () => {
+ try {
+ await apple.validateAuthData(
+ { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' },
+ { clientId: 'INSERT CLIENT ID HERE' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it('should throw error with with invalid user id (apple.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://appleid.apple.com',
+ aud: 'invalid_client_id',
+ sub: 'a_different_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await apple.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { clientId: 'secret' }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it('should throw error when clientId is not configured', async () => {
+ try {
+ await apple.validateAuthData({ id: 'the_user_id', token: 'the_token' }, {});
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Apple auth is not configured.');
+ }
+ });
+});
+
+describe('phant auth adapter', () => {
+ const httpsRequest = require('../lib/Adapters/Auth/httpsRequest');
+
+ it('validateAuthData should throw for invalid auth', async () => {
+ await reconfigureServer({
+ auth: {
+ phantauth: {
+ enableInsecureAuth: true,
+ }
+ }
+ })
+ const authData = {
+ id: 'fakeid',
+ access_token: 'sometoken',
+ };
+ const { adapter } = authenticationLoader.loadAuthAdapter('phantauth', {});
+
+ spyOn(httpsRequest, 'get').and.callFake(() => Promise.resolve({ sub: 'invalidID' }));
+ try {
+ await adapter.validateAuthData(authData);
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('PhantAuth auth is invalid for this user.');
+ }
+ });
+});
+
+describe('facebook limited auth adapter', () => {
+ const facebook = require('../lib/Adapters/Auth/facebook');
+ const jwt = require('jsonwebtoken');
+ const authUtils = require('../lib/Adapters/Auth/utils');
+
+ // TODO: figure out a way to run this test alongside facebook classic tests
+ xit('should throw error with missing id_token', async () => {
+ try {
+ await facebook.validateAuthData({}, { appIds: ['secret'] });
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('Facebook auth is not configured.');
+ }
+ });
+
+ it('should not decode invalid id_token', async () => {
+ try {
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('provided token does not decode as JWT');
+ }
+ });
+
+ it('should throw error if public key used to encode token is not available', async () => {
+ const fakeDecodedToken = {
+ header: { kid: '789', alg: 'RS256' },
+ };
+ try {
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ `Unable to find matching key for Key ID: ${fakeDecodedToken.header.kid}`
+ );
+ }
+ });
+
+ it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)('should use algorithm from key header to verify id_token (facebook.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://www.facebook.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ expect(result).toEqual(fakeClaim);
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('should pass hardcoded RS256 algorithm to jwt.verify, not the JWT header alg (GHSA-4q3h-vp4r-prv2)', async () => {
+ const fakeClaim = {
+ iss: 'https://www.facebook.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { kid: '123', alg: 'none' };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ expect(jwt.verify.calls.first().args[2].algorithms).toEqual(['RS256']);
+ });
+
+ it('should not verify invalid id_token', async () => {
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+
+ try {
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt malformed');
+ }
+ });
+
+ it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)('should verify id_token (facebook.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://www.facebook.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)('(using multiple appIds) should verify id_token (facebook.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://www.facebook.com',
+ aud: 'secret',
+ exp: Date.now(),
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ const result = await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret', 'secret 123'] }
+ );
+ expect(result).toEqual(fakeClaim);
+ });
+
+ it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)('should throw error with with invalid jwt issuer (facebook.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://not.facebook.com',
+ sub: 'the_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe(
+ 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
+ );
+ }
+ });
+
+ // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
+ // and a private key
+ xit('should throw error with invalid jwt audience', async () => {
+ try {
+ await facebook.validateAuthData(
+ {
+ id: 'INSERT ID HERE',
+ token: 'INSERT FACEBOOK TOKEN HERE',
+ },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('jwt audience invalid. expected: secret');
+ }
+ });
+
+ // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
+ // and a private key
+ xit('should throw error with invalid user id', async () => {
+ try {
+ await facebook.validateAuthData(
+ {
+ id: 'invalid user',
+ token: 'INSERT FACEBOOK TOKEN HERE',
+ },
+ { appIds: ['INSERT APP ID HERE'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)('should throw error with with invalid user id (facebook.com)', async () => {
+ const fakeClaim = {
+ iss: 'https://www.facebook.com',
+ aud: 'invalid_app_id',
+ sub: 'a_different_user_id',
+ };
+ const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
+ const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
+ spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
+ spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
+ spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
+
+ try {
+ await facebook.validateAuthData(
+ { id: 'the_user_id', token: 'the_token' },
+ { appIds: ['secret'] }
+ );
+ fail();
+ } catch (e) {
+ expect(e.message).toBe('auth data is invalid for this user.');
+ }
+ });
+
+ it('should throw error when appIds is not configured for Limited Login', async () => {
+ try {
+ await facebook.validateAuthData({ id: 'the_user_id', token: 'the_token' }, {});
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Facebook auth is not configured.');
+ }
+ });
+
+ it('should throw error when appIds is not configured for Standard Login', async () => {
+ try {
+ await facebook.validateAuthData({ id: 'the_user_id', access_token: 'the_token' }, {});
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Facebook auth is not configured.');
+ }
+ });
+
+ it('should throw error when appIds is empty array for Standard Login', async () => {
+ try {
+ await facebook.validateAuthData({ id: 'the_user_id', access_token: 'the_token' }, { appIds: [] });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toBe('Facebook auth is not configured.');
+ }
+ });
+});
+
+describe('OTP TOTP auth adatper', () => {
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+ beforeEach(async () => {
+ await reconfigureServer({
+ auth: {
+ mfa: {
+ enabled: true,
+ options: ['TOTP'],
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ },
+ },
+ });
+ });
+
+ it('can enroll', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ const response = user.get('authDataResponse');
+ expect(response.mfa).toBeDefined();
+ expect(response.mfa.recovery).toBeDefined();
+ expect(response.mfa.recovery.split(',').length).toEqual(2);
+ await user.fetch();
+ expect(user.get('authData').mfa).toEqual({ status: 'enabled' });
+ });
+
+ it('can login with valid token', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ const response = await request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: totp.generate(),
+ },
+ },
+ }),
+ }).then(res => res.data);
+ expect(response.objectId).toEqual(user.id);
+ expect(response.sessionToken).toBeDefined();
+ expect(response.authData).toEqual({ mfa: { status: 'enabled' } });
+ expect(Object.keys(response).sort()).toEqual(
+ [
+ 'objectId',
+ 'username',
+ 'createdAt',
+ 'updatedAt',
+ 'authData',
+ 'ACL',
+ 'sessionToken',
+ 'authDataResponse',
+ ].sort()
+ );
+ });
+
+ it('can change OTP with valid token', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ const new_secret = new OTPAuth.Secret();
+ const new_totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret: new_secret,
+ });
+ const new_token = new_totp.generate();
+ await user.save(
+ {
+ authData: { mfa: { secret: new_secret.base32, token: new_token, old: totp.generate() } },
+ },
+ { sessionToken: user.getSessionToken() }
+ );
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData').mfa.secret).toEqual(new_secret.base32);
+ });
+
+ it('cannot change OTP with invalid token', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ const new_secret = new OTPAuth.Secret();
+ const new_totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret: new_secret,
+ });
+ const new_token = new_totp.generate();
+ await expectAsync(
+ user.save(
+ {
+ authData: { mfa: { secret: new_secret.base32, token: new_token, old: '123' } },
+ },
+ { sessionToken: user.getSessionToken() }
+ )
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.OTHER_CAUSE, 'Invalid MFA token'));
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData').mfa.secret).toEqual(secret.base32);
+ });
+
+ it('future logins require TOTP token', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa')
+ );
+ });
+
+ it('consumes recovery code after use', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ // Get recovery codes from stored auth data
+ await user.fetch({ useMasterKey: true });
+ const recoveryCode = user.get('authData').mfa.recovery[0];
+ // First login with recovery code should succeed
+ await request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: recoveryCode,
+ },
+ },
+ }),
+ });
+ // Second login with same recovery code should fail (code consumed)
+ await expectAsync(
+ request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: recoveryCode,
+ },
+ },
+ }),
+ }).catch(e => {
+ throw e.data;
+ })
+ ).toBeRejectedWith({ code: Parse.Error.SCRIPT_FAILED, error: 'Invalid MFA token' });
+ });
+
+ it('future logins reject incorrect TOTP token', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ await expectAsync(
+ request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: 'abcd',
+ },
+ },
+ }),
+ }).catch(e => {
+ throw e.data;
+ })
+ ).toBeRejectedWith({ code: Parse.Error.SCRIPT_FAILED, error: 'Invalid MFA token' });
+ });
+
+ it('allows unlinking MFA without TOTP verification (by design)', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const sessionToken = user.getSessionToken();
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ // Enable MFA
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken }
+ );
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData').mfa.secret).toBeDefined();
+ // Unlink MFA without providing TOTP
+ await user.save(
+ { authData: { mfa: null } },
+ { sessionToken }
+ );
+ // MFA should be removed
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toBeUndefined();
+ // Login should succeed without MFA
+ const response = await request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ }),
+ });
+ expect(response.data.sessionToken).toBeDefined();
+ });
+
+ it('allows blocking MFA unlink via beforeSave trigger', async () => {
+ Parse.Cloud.beforeSave('_User', request => {
+ const authData = request.object.get('authData');
+ if (authData?.mfa === null) {
+ throw new Parse.Error(Parse.Error.VALIDATION_ERROR, 'Cannot disable MFA without verification');
+ }
+ });
+ const user = await Parse.User.signUp('username', 'password');
+ const OTPAuth = require('otpauth');
+ const secret = new OTPAuth.Secret();
+ const totp = new OTPAuth.TOTP({
+ algorithm: 'SHA1',
+ digits: 6,
+ period: 30,
+ secret,
+ });
+ const token = totp.generate();
+ // Enable MFA
+ await user.save(
+ { authData: { mfa: { secret: secret.base32, token } } },
+ { sessionToken: user.getSessionToken() }
+ );
+ // Attempt to unlink MFA â should be blocked by beforeSave trigger
+ await expectAsync(
+ user.save(
+ { authData: { mfa: null } },
+ { sessionToken: user.getSessionToken() }
+ )
+ ).toBeRejectedWith(
+ new Parse.Error(Parse.Error.VALIDATION_ERROR, 'Cannot disable MFA without verification')
+ );
+ // MFA should still be enabled
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData').mfa.secret).toBeDefined();
+ });
+});
+
+describe('OTP SMS auth adatper', () => {
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+ let code;
+ let mobile;
+ const mfa = {
+ enabled: true,
+ options: ['SMS'],
+ sendSMS(smsCode, number) {
+ expect(smsCode).toBeDefined();
+ expect(number).toBeDefined();
+ expect(smsCode.length).toEqual(6);
+ code = smsCode;
+ mobile = number;
+ },
+ digits: 6,
+ period: 30,
+ };
+ beforeEach(async () => {
+ code = '';
+ mobile = '';
+ await reconfigureServer({
+ auth: {
+ mfa,
+ },
+ });
+ });
+
+ it('can enroll', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const sessionToken = user.getSessionToken();
+ const spy = spyOn(mfa, 'sendSMS').and.callThrough();
+ await user.save({ authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken });
+ await user.fetch({ sessionToken });
+ expect(user.get('authData')).toEqual({ mfa: { status: 'disabled' } });
+ expect(spy).toHaveBeenCalledWith(code, '+11111111111');
+ await user.fetch({ useMasterKey: true });
+ const authData = user.get('authData').mfa?.pending;
+ expect(authData).toBeDefined();
+ expect(authData['+11111111111']).toBeDefined();
+ expect(Object.keys(authData['+11111111111'])).toEqual(['token', 'expiry']);
+
+ await user.save({ authData: { mfa: { mobile, token: code } } }, { sessionToken });
+ await user.fetch({ sessionToken });
+ expect(user.get('authData')).toEqual({ mfa: { status: 'enabled' } });
+ });
+
+ it('future logins require SMS code', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ const spy = spyOn(mfa, 'sendSMS').and.callThrough();
+ await user.save(
+ { authData: { mfa: { mobile: '+11111111111' } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ await user.save(
+ { authData: { mfa: { mobile, token: code } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ spy.calls.reset();
+
+ await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith(
+ new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa')
+ );
+ const res = await request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: 'request',
+ },
+ },
+ }),
+ }).catch(e => e.data);
+ expect(res).toEqual({ code: Parse.Error.SCRIPT_FAILED, error: 'Please enter the token' });
+ expect(spy).toHaveBeenCalledWith(code, '+11111111111');
+ const response = await request({
+ headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ mfa: {
+ token: code,
+ },
+ },
+ }),
+ }).then(res => res.data);
+ expect(response.objectId).toEqual(user.id);
+ expect(response.sessionToken).toBeDefined();
+ expect(response.authData).toEqual({ mfa: { status: 'enabled' } });
+ expect(Object.keys(response).sort()).toEqual(
+ [
+ 'objectId',
+ 'username',
+ 'createdAt',
+ 'updatedAt',
+ 'authData',
+ 'ACL',
+ 'sessionToken',
+ 'authDataResponse',
+ ].sort()
+ );
+ });
+
+ it('partially enrolled users can still login', async () => {
+ const user = await Parse.User.signUp('username', 'password');
+ await user.save({ authData: { mfa: { mobile: '+11111111111' } } });
+ const spy = spyOn(mfa, 'sendSMS').and.callThrough();
+ await Parse.User.logIn('username', 'password');
+ expect(spy).not.toHaveBeenCalled();
+ });
+});
diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js
new file mode 100644
index 0000000000..cafb309f0b
--- /dev/null
+++ b/spec/AuthenticationAdaptersV2.spec.js
@@ -0,0 +1,1705 @@
+const request = require('../lib/request');
+const Auth = require('../lib/Auth');
+const Config = require('../lib/Config');
+const requestWithExpectedError = async params => {
+ try {
+ return await request(params);
+ } catch (e) {
+ throw new Error(e.data.error);
+ }
+};
+describe('Auth Adapter features', () => {
+ const baseAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+ const baseAdapter2 = {
+ validateAppId: appIds => (appIds[0] === 'test' ? Promise.resolve() : Promise.reject()),
+ validateAuthData: () => Promise.resolve(),
+ appIds: ['test'],
+ options: { anOption: true },
+ };
+
+ const doNotSaveAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve({ doNotSave: true }),
+ };
+
+ const additionalAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ policy: 'additional',
+ };
+
+ const soloAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ policy: 'solo',
+ };
+
+ const challengeAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ challenge: () => Promise.resolve({ token: 'test' }),
+ options: {
+ anOption: true,
+ },
+ };
+
+ const modernAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.resolve(),
+ };
+
+ const modernAdapter2 = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.resolve(),
+ };
+
+ const modernAdapter3 = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.resolve(),
+ validateOptions: () => Promise.resolve(),
+ afterFind() {
+ return {
+ foo: 'bar',
+ };
+ },
+ };
+
+ const wrongAdapter = {
+ validateAppId: () => Promise.resolve(),
+ };
+
+ // Code-based adapter that requires 'code' field (like gpgames)
+ const codeBasedAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: authData => {
+ if (!authData.code) {
+ throw new Error('code is required.');
+ }
+ return Promise.resolve({ save: { id: authData.id } });
+ },
+ validateUpdate: authData => {
+ if (!authData.code) {
+ throw new Error('code is required.');
+ }
+ return Promise.resolve({ save: { id: authData.id } });
+ },
+ validateLogin: authData => {
+ if (!authData.code) {
+ throw new Error('code is required.');
+ }
+ return Promise.resolve({ save: { id: authData.id } });
+ },
+ afterFind: authData => {
+ // Strip sensitive 'code' field when returning to client
+ return { id: authData.id };
+ },
+ };
+
+ // Simple adapter that doesn't require code
+ const simpleAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.resolve(),
+ };
+
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+
+ it('should ensure no duplicate auth data id after before save', async () => {
+ await reconfigureServer({
+ auth: { baseAdapter },
+ cloud: () => {
+ Parse.Cloud.beforeSave('_User', async request => {
+ request.object.set('authData', { baseAdapter: { id: 'test' } });
+ });
+ },
+ });
+
+ const user = new Parse.User();
+ await user.save({ authData: { baseAdapter: { id: 'another' } } });
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } });
+
+ const user2 = new Parse.User();
+ await expectAsync(
+ user2.save({ authData: { baseAdapter: { id: 'another' } } })
+ ).toBeRejectedWithError('this auth is already used');
+ });
+
+ it('should ensure no duplicate auth data id after before save in case of more than one result', async () => {
+ await reconfigureServer({
+ auth: { baseAdapter },
+ cloud: () => {
+ Parse.Cloud.beforeSave('_User', async request => {
+ request.object.set('authData', { baseAdapter: { id: 'test' } });
+ });
+ },
+ });
+
+ const user = new Parse.User();
+ await user.save({ authData: { baseAdapter: { id: 'another' } } });
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } });
+
+ let i = 0;
+ const originalFn = Auth.findUsersWithAuthData;
+ spyOn(Auth, 'findUsersWithAuthData').and.callFake((...params) => {
+ // First call is triggered during authData validation
+ if (i === 0) {
+ i++;
+ return originalFn(...params);
+ }
+ // Second call is triggered after beforeSave. A developer can modify authData during beforeSave.
+ // To perform a determinist login, the uniqueness of `auth.id` needs to be ensured.
+ // A developer with a direct access to the database could break something and duplicate authData.id.
+ // In this case, if 2 matching users are detected for a single authData.id, then the login/register will be canceled.
+ // Promise.resolve([true, true]) simulates this case with 2 matching users.
+ return Promise.resolve([true, true]);
+ });
+ const user2 = new Parse.User();
+ await expectAsync(
+ user2.save({ authData: { baseAdapter: { id: 'another' } } })
+ ).toBeRejectedWithError('this auth is already used');
+ });
+
+ it('should ensure no duplicate auth data id during authData validation in case of more than one result', async () => {
+ await reconfigureServer({
+ auth: { baseAdapter },
+ cloud: () => {
+ Parse.Cloud.beforeSave('_User', async request => {
+ request.object.set('authData', { baseAdapter: { id: 'test' } });
+ });
+ },
+ });
+
+ spyOn(Auth, 'findUsersWithAuthData').and.resolveTo([true, true]);
+
+ const user = new Parse.User();
+ await expectAsync(
+ user.save({ authData: { baseAdapter: { id: 'another' } } })
+ ).toBeRejectedWithError('this auth is already used');
+ });
+
+ it('should pass authData, options, request to validateAuthData', async () => {
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({});
+ await reconfigureServer({ auth: { baseAdapter } });
+ const user = new Parse.User();
+ const payload = { someData: true };
+
+ await user.save({
+ username: 'test',
+ password: 'password',
+ authData: { baseAdapter: payload },
+ });
+
+ expect(user.getSessionToken()).toBeDefined();
+ const firstCall = baseAdapter.validateAuthData.calls.argsFor(0);
+ expect(firstCall[0]).toEqual(payload);
+ expect(firstCall[1]).toEqual(baseAdapter);
+ expect(firstCall[2].object).toBeDefined();
+ expect(firstCall[2].original).toBeUndefined();
+ expect(firstCall[2].user).toBeUndefined();
+ expect(firstCall[2].isChallenge).toBeUndefined();
+ expect(firstCall.length).toEqual(3);
+
+ await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'test',
+ password: 'password',
+ authData: { baseAdapter: payload },
+ }),
+ });
+ const secondCall = baseAdapter.validateAuthData.calls.argsFor(1);
+ expect(secondCall[0]).toEqual(payload);
+ expect(secondCall[1]).toEqual(baseAdapter);
+ expect(secondCall[2].original).toBeDefined();
+ expect(secondCall[2].original instanceof Parse.User).toBeTruthy();
+ expect(secondCall[2].original.id).toEqual(user.id);
+ expect(secondCall[2].object).toBeDefined();
+ expect(secondCall[2].object instanceof Parse.User).toBeTruthy();
+ expect(secondCall[2].object.id).toEqual(user.id);
+ expect(secondCall[2].isChallenge).toBeUndefined();
+ expect(secondCall[2].user).toBeUndefined();
+ expect(secondCall.length).toEqual(3);
+ });
+
+ it('should trigger correctly validateSetUp', async () => {
+ spyOn(modernAdapter, 'validateSetUp').and.resolveTo({});
+ spyOn(modernAdapter, 'validateUpdate').and.resolveTo({});
+ spyOn(modernAdapter, 'validateLogin').and.resolveTo({});
+ spyOn(modernAdapter2, 'validateSetUp').and.resolveTo({});
+ spyOn(modernAdapter2, 'validateUpdate').and.resolveTo({});
+ spyOn(modernAdapter2, 'validateLogin').and.resolveTo({});
+
+ await reconfigureServer({ auth: { modernAdapter, modernAdapter2 } });
+ const user = new Parse.User();
+
+ await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } });
+
+ expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0);
+ expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0);
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+ const call = modernAdapter.validateSetUp.calls.argsFor(0);
+ expect(call[0]).toEqual({ id: 'modernAdapter' });
+ expect(call[1]).toEqual(modernAdapter);
+ expect(call[2].isChallenge).toBeUndefined();
+ expect(call[2].master).toBeDefined();
+ expect(call[2].object instanceof Parse.User).toBeTruthy();
+ expect(call[2].user).toBeUndefined();
+ expect(call[2].original).toBeUndefined();
+ expect(call[2].triggerName).toBe('validateSetUp');
+ expect(call.length).toEqual(3);
+ expect(user.getSessionToken()).toBeDefined();
+
+ await user.save(
+ { authData: { modernAdapter2: { id: 'modernAdapter2' } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ expect(modernAdapter2.validateUpdate).toHaveBeenCalledTimes(0);
+ expect(modernAdapter2.validateLogin).toHaveBeenCalledTimes(0);
+ expect(modernAdapter2.validateSetUp).toHaveBeenCalledTimes(1);
+ const call2 = modernAdapter2.validateSetUp.calls.argsFor(0);
+ expect(call2[0]).toEqual({ id: 'modernAdapter2' });
+ expect(call2[1]).toEqual(modernAdapter2);
+ expect(call2[2].isChallenge).toBeUndefined();
+ expect(call2[2].master).toBeDefined();
+ expect(call2[2].object instanceof Parse.User).toBeTruthy();
+ expect(call2[2].original instanceof Parse.User).toBeTruthy();
+ expect(call2[2].user instanceof Parse.User).toBeTruthy();
+ expect(call2[2].original.id).toEqual(call2[2].object.id);
+ expect(call2[2].user.id).toEqual(call2[2].object.id);
+ expect(call2[2].object.id).toEqual(user.id);
+ expect(call2[2].triggerName).toBe('validateSetUp');
+ expect(call2.length).toEqual(3);
+
+ const user2 = new Parse.User();
+ user2.id = user.id;
+ await user2.fetch({ useMasterKey: true });
+ expect(user2.get('authData')).toEqual({
+ modernAdapter: { id: 'modernAdapter' },
+ modernAdapter2: { id: 'modernAdapter2' },
+ });
+ });
+
+ it('should trigger correctly validateLogin', async () => {
+ spyOn(modernAdapter, 'validateSetUp').and.resolveTo({});
+ spyOn(modernAdapter, 'validateUpdate').and.resolveTo({});
+ spyOn(modernAdapter, 'validateLogin').and.resolveTo({});
+
+ await reconfigureServer({ auth: { modernAdapter }, allowExpiredAuthDataToken: false });
+ const user = new Parse.User();
+
+ await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } });
+
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+ const user2 = new Parse.User();
+ await user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } });
+
+ expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0);
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+ expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(1);
+ const call = modernAdapter.validateLogin.calls.argsFor(0);
+ expect(call[0]).toEqual({ id: 'modernAdapter' });
+ expect(call[1]).toEqual(modernAdapter);
+ expect(call[2].object instanceof Parse.User).toBeTruthy();
+ expect(call[2].original instanceof Parse.User).toBeTruthy();
+ expect(call[2].isChallenge).toBeUndefined();
+ expect(call[2].master).toBeDefined();
+ expect(call[2].user).toBeUndefined();
+ expect(call[2].original.id).toEqual(user2.id);
+ expect(call[2].object.id).toEqual(user2.id);
+ expect(call[2].object.id).toEqual(user.id);
+ expect(call.length).toEqual(3);
+ expect(user2.getSessionToken()).toBeDefined();
+ });
+
+ it('should trigger correctly validateUpdate', async () => {
+ spyOn(modernAdapter, 'validateSetUp').and.resolveTo({});
+ spyOn(modernAdapter, 'validateUpdate').and.resolveTo({});
+ spyOn(modernAdapter, 'validateLogin').and.resolveTo({});
+
+ await reconfigureServer({ auth: { modernAdapter } });
+ const user = new Parse.User();
+
+ await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } });
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+
+ // Save same data
+ await user.save(
+ { authData: { modernAdapter: { id: 'modernAdapter' } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ // Save same data with master key
+ await user.save(
+ { authData: { modernAdapter: { id: 'modernAdapter' } } },
+ { useMasterKey: true }
+ );
+
+ expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0);
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+ expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0);
+
+ // Change authData
+ await user.save(
+ { authData: { modernAdapter: { id: 'modernAdapter2' } } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(1);
+ expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1);
+ expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0);
+ const call = modernAdapter.validateUpdate.calls.argsFor(0);
+ expect(call[0]).toEqual({ id: 'modernAdapter2' });
+ expect(call[1]).toEqual(modernAdapter);
+ expect(call[2].isChallenge).toBeUndefined();
+ expect(call[2].master).toBeDefined();
+ expect(call[2].object instanceof Parse.User).toBeTruthy();
+ expect(call[2].user instanceof Parse.User).toBeTruthy();
+ expect(call[2].original instanceof Parse.User).toBeTruthy();
+ expect(call[2].object.id).toEqual(user.id);
+ expect(call[2].original.id).toEqual(user.id);
+ expect(call[2].user.id).toEqual(user.id);
+ expect(call.length).toEqual(3);
+ expect(user.getSessionToken()).toBeDefined();
+ });
+
+ it('should strip out authData if required', async () => {
+ const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough();
+ const afterSpy = spyOn(modernAdapter3, 'afterFind').and.callThrough();
+ await reconfigureServer({ auth: { modernAdapter3 } });
+ const user = new Parse.User();
+ await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } });
+ await user.fetch({ sessionToken: user.getSessionToken() });
+ const authData = user.get('authData').modernAdapter3;
+ expect(authData).toEqual({ foo: 'bar' });
+ for (const call of afterSpy.calls.all()) {
+ const args = call.args[2];
+ if (args.user) {
+ user._objCount = args.user._objCount;
+ break;
+ }
+ }
+ expect(afterSpy).toHaveBeenCalledWith(
+ { id: 'modernAdapter3Data' },
+ undefined,
+ { ip: '127.0.0.1', user, master: false },
+ );
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('should throw if policy does not match one of default/solo/additional', async () => {
+ const adapterWithBadPolicy = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ policy: 'bad',
+ };
+ await reconfigureServer({ auth: { adapterWithBadPolicy } });
+ const user = new Parse.User();
+ await expectAsync(
+ user.save({ authData: { adapterWithBadPolicy: { id: 'adapterWithBadPolicy' } } })
+ ).toBeRejectedWithError(
+ 'AuthAdapter policy is not configured correctly. The value must be either "solo", "additional", "default" or undefined (will be handled as "default")'
+ );
+ });
+
+ it('should throw if no triggers found', async () => {
+ await reconfigureServer({ auth: { wrongAdapter } });
+ const user = new Parse.User();
+ await expectAsync(
+ user.save({ authData: { wrongAdapter: { id: 'wrongAdapter' } } })
+ ).toBeRejectedWithError(
+ 'Adapter is not configured. Implement either validateAuthData or all of the following: validateSetUp, validateLogin and validateUpdate'
+ );
+ });
+
+ it('should not update authData if provider return doNotSave', async () => {
+ spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: true });
+ await reconfigureServer({
+ auth: { doNotSaveAdapter, baseAdapter },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: { baseAdapter: { id: 'baseAdapter' }, doNotSaveAdapter: { token: true } },
+ });
+
+ await user.fetch({ useMasterKey: true });
+
+ expect(user.get('authData')).toEqual({ baseAdapter: { id: 'baseAdapter' } });
+ });
+
+ it('should loginWith user with auth Adapter with do not save option, mutated authData and no additional auth adapter', async () => {
+ const spy = spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: false });
+ await reconfigureServer({
+ auth: { doNotSaveAdapter, baseAdapter },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: { doNotSaveAdapter: { id: 'doNotSaveAdapter' } },
+ });
+
+ await user.fetch({ useMasterKey: true });
+
+ expect(user.get('authData')).toEqual({ doNotSaveAdapter: { id: 'doNotSaveAdapter' } });
+
+ spy.and.resolveTo({ doNotSave: true });
+
+ const user2 = await Parse.User.logInWith('doNotSaveAdapter', {
+ authData: { id: 'doNotSaveAdapter', example: 'example' },
+ });
+ expect(user2.getSessionToken()).toBeDefined();
+ expect(user2.id).toEqual(user.id);
+ });
+
+ it('should perform authData validation only when its required', async () => {
+ spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({});
+ spyOn(baseAdapter2, 'validateAppId').and.resolveTo({});
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({});
+ await reconfigureServer({
+ auth: { baseAdapter2, baseAdapter },
+ allowExpiredAuthDataToken: false,
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { token: true },
+ },
+ });
+
+ expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(1);
+ expect(baseAdapter2.validateAppId).toHaveBeenCalledTimes(1);
+
+ const user2 = new Parse.User();
+ await user2.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ },
+ });
+
+ expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(1);
+
+ const user3 = new Parse.User();
+ await user3.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { token: true },
+ },
+ });
+
+ expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(2);
+ });
+
+ it('should not perform authData validation twice when data mutated', async () => {
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({});
+ await reconfigureServer({
+ auth: { baseAdapter },
+ allowExpiredAuthDataToken: false,
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter', token: "sometoken1" },
+ },
+ });
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1);
+
+ const user2 = new Parse.User();
+ await user2.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter', token: "sometoken2" },
+ },
+ });
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2);
+ });
+
+ it('should require additional provider if configured', async () => {
+ await reconfigureServer({
+ auth: { baseAdapter, additionalAdapter },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ additionalAdapter: { token: true },
+ },
+ });
+
+ const user2 = new Parse.User();
+ await expectAsync(
+ user2.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ },
+ })
+ ).toBeRejectedWithError('Missing additional authData additionalAdapter');
+ expect(user2.getSessionToken()).toBeUndefined();
+
+ await user2.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ additionalAdapter: { token: true },
+ },
+ });
+
+ expect(user2.getSessionToken()).toBeDefined();
+ });
+
+ it('should skip additional provider if used provider is solo', async () => {
+ await reconfigureServer({
+ auth: { soloAdapter, additionalAdapter },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: {
+ soloAdapter: { id: 'soloAdapter' },
+ additionalAdapter: { token: true },
+ },
+ });
+
+ const user2 = new Parse.User();
+ await user2.save({
+ authData: {
+ soloAdapter: { id: 'soloAdapter' },
+ },
+ });
+ expect(user2.getSessionToken()).toBeDefined();
+ });
+
+ it('should return authData response and save some info on non username login', async () => {
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({
+ response: { someData: true },
+ });
+ spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({
+ response: { someData2: true },
+ save: { otherData: true },
+ });
+ await reconfigureServer({
+ auth: { baseAdapter, baseAdapter2 },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+
+ expect(user.get('authDataResponse')).toEqual({
+ baseAdapter: { someData: true },
+ baseAdapter2: { someData2: true },
+ });
+
+ const user2 = new Parse.User();
+ user2.id = user.id;
+ await user2.save(
+ {
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ expect(user2.get('authDataResponse')).toEqual({ baseAdapter2: { someData2: true } });
+
+ const user3 = new Parse.User();
+ await user3.save({
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+
+ // On logIn all authData are revalidated
+ expect(user3.get('authDataResponse')).toEqual({
+ baseAdapter: { someData: true },
+ baseAdapter2: { someData2: true },
+ });
+
+ const userViaMasterKey = new Parse.User();
+ userViaMasterKey.id = user2.id;
+ await userViaMasterKey.fetch({ useMasterKey: true });
+ expect(userViaMasterKey.get('authData')).toEqual({
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { otherData: true },
+ });
+ });
+
+ it('should return authData response and save some info on username login', async () => {
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({
+ response: { someData: true },
+ });
+ spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({
+ response: { someData2: true },
+ save: { otherData: true },
+ });
+ await reconfigureServer({
+ auth: { baseAdapter, baseAdapter2 },
+ });
+
+ const user = new Parse.User();
+
+ await user.save({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+
+ expect(user.get('authDataResponse')).toEqual({
+ baseAdapter: { someData: true },
+ baseAdapter2: { someData2: true },
+ });
+
+ const res = await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/login',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter2: { test: true },
+ baseAdapter: { id: 'baseAdapter' },
+ },
+ }),
+ });
+ const result = res.data;
+ expect(result.authDataResponse).toEqual({
+ baseAdapter2: { someData2: true },
+ baseAdapter: { someData: true },
+ });
+
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { otherData: true },
+ });
+ });
+
+ describe('should allow update of authData', () => {
+ beforeEach(async () => {
+ spyOn(baseAdapter, 'validateAuthData').and.resolveTo({
+ response: { someData: true },
+ });
+ spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({
+ response: { someData2: true },
+ save: { otherData: true },
+ });
+ await reconfigureServer({
+ auth: { baseAdapter, baseAdapter2 },
+ });
+ });
+
+ it('should not re validate the baseAdapter when user is already logged in and authData not changed', async () => {
+ const user = new Parse.User();
+
+ await user.save({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1);
+
+ expect(user.id).toBeDefined();
+ expect(user.getSessionToken()).toBeDefined();
+ await user.save(
+ {
+ authData: {
+ baseAdapter2: { test: true },
+ baseAdapter: { id: 'baseAdapter' },
+ },
+ },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not re-validate the baseAdapter when master key is used and authData has not changed', async () => {
+ const user = new Parse.User();
+ await user.save({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+ await user.save(
+ {
+ authData: {
+ baseAdapter2: { test: true },
+ baseAdapter: { id: 'baseAdapter' },
+ },
+ },
+ { useMasterKey: true }
+ );
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1);
+ });
+
+ it('should allow user to change authData', async () => {
+ const user = new Parse.User();
+ await user.save({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+ await user.save(
+ {
+ authData: {
+ baseAdapter2: { test: true },
+ baseAdapter: { id: 'baseAdapter2' },
+ },
+ },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2);
+ });
+
+ it('should allow master key to change authData', async () => {
+ const user = new Parse.User();
+ await user.save({
+ username: 'username',
+ password: 'password',
+ authData: {
+ baseAdapter: { id: 'baseAdapter' },
+ baseAdapter2: { test: true },
+ },
+ });
+ await user.save(
+ {
+ authData: {
+ baseAdapter2: { test: true },
+ baseAdapter: { id: 'baseAdapter3' },
+ },
+ },
+ { useMasterKey: true }
+ );
+
+ expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2);
+
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({
+ baseAdapter: { id: 'baseAdapter3' },
+ baseAdapter2: { otherData: true },
+ });
+ });
+ });
+
+ it('should pass user to auth adapter on update by matching session', async () => {
+ spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({});
+ await reconfigureServer({ auth: { baseAdapter2 } });
+
+ const user = new Parse.User();
+
+ const payload = { someData: true };
+
+ await user.save({
+ username: 'test',
+ password: 'password',
+ });
+
+ expect(user.getSessionToken()).toBeDefined();
+
+ await user.save(
+ { authData: { baseAdapter2: payload } },
+ { sessionToken: user.getSessionToken() }
+ );
+
+ const firstCall = baseAdapter2.validateAuthData.calls.argsFor(0);
+ expect(firstCall[0]).toEqual(payload);
+ expect(firstCall[1]).toEqual(baseAdapter2);
+ expect(firstCall[2].isChallenge).toBeUndefined();
+ expect(firstCall[2].master).toBeDefined();
+ expect(firstCall[2].object instanceof Parse.User).toBeTruthy();
+ expect(firstCall[2].user instanceof Parse.User).toBeTruthy();
+ expect(firstCall[2].original instanceof Parse.User).toBeTruthy();
+ expect(firstCall[2].object.id).toEqual(user.id);
+ expect(firstCall[2].original.id).toEqual(user.id);
+ expect(firstCall[2].user.id).toEqual(user.id);
+ expect(firstCall.length).toEqual(3);
+
+ await user.save({ authData: { baseAdapter2: payload } }, { useMasterKey: true });
+
+ const secondCall = baseAdapter2.validateAuthData.calls.argsFor(1);
+ expect(secondCall[0]).toEqual(payload);
+ expect(secondCall[1]).toEqual(baseAdapter2);
+ expect(secondCall[2].isChallenge).toBeUndefined();
+ expect(secondCall[2].master).toEqual(true);
+ expect(secondCall[2].user).toBeUndefined();
+ expect(secondCall[2].object instanceof Parse.User).toBeTruthy();
+ expect(secondCall[2].original instanceof Parse.User).toBeTruthy();
+ expect(secondCall[2].object.id).toEqual(user.id);
+ expect(secondCall[2].original.id).toEqual(user.id);
+ expect(secondCall.length).toEqual(3);
+ });
+
+ it('should return custom errors', async () => {
+ const throwInChallengeAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ challenge: () => Promise.reject('Invalid challenge data: yolo'),
+ options: {
+ anOption: true,
+ },
+ };
+ const throwInSetup = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.reject('You cannot signup with that setup data.'),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.resolve(),
+ };
+
+ const throwInUpdate = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.reject('You cannot update with that update data.'),
+ validateLogin: () => Promise.resolve(),
+ };
+
+ const throwInLogin = {
+ validateAppId: () => Promise.resolve(),
+ validateSetUp: () => Promise.resolve(),
+ validateUpdate: () => Promise.resolve(),
+ validateLogin: () => Promise.reject('You cannot login with that login data.'),
+ };
+ await reconfigureServer({
+ auth: { challengeAdapter: throwInChallengeAdapter },
+ });
+ let logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('Invalid challenge data: yolo');
+ expect(logger.error).toHaveBeenCalledWith(
+ `Failed running auth step challenge for challengeAdapter for user undefined with Error: {"message":"Invalid challenge data: yolo","code":${Parse.Error.SCRIPT_FAILED}}`,
+ {
+ authenticationStep: 'challenge',
+ error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid challenge data: yolo'),
+ user: undefined,
+ provider: 'challengeAdapter',
+ }
+ );
+
+ await reconfigureServer({ auth: { modernAdapter: throwInSetup } });
+ logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ let user = new Parse.User();
+ await expectAsync(
+ user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } })
+ ).toBeRejectedWith(
+ new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot signup with that setup data.')
+ );
+ expect(logger.error).toHaveBeenCalledWith(
+ `Failed running auth step validateSetUp for modernAdapter for user undefined with Error: {"message":"You cannot signup with that setup data.","code":${Parse.Error.SCRIPT_FAILED}}`,
+ {
+ authenticationStep: 'validateSetUp',
+ error: new Parse.Error(
+ Parse.Error.SCRIPT_FAILED,
+ 'You cannot signup with that setup data.'
+ ),
+ user: undefined,
+ provider: 'modernAdapter',
+ }
+ );
+
+ await reconfigureServer({ auth: { modernAdapter: throwInUpdate } });
+ logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ user = new Parse.User();
+ await user.save({ authData: { modernAdapter: { id: 'updateAdapter' } } });
+ await expectAsync(
+ user.save(
+ { authData: { modernAdapter: { id: 'updateAdapter2' } } },
+ { sessionToken: user.getSessionToken() }
+ )
+ ).toBeRejectedWith(
+ new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot update with that update data.')
+ );
+
+ expect(logger.error).toHaveBeenCalledWith(
+ `Failed running auth step validateUpdate for modernAdapter for user ${user.id} with Error: {"message":"You cannot update with that update data.","code":${Parse.Error.SCRIPT_FAILED}}`,
+ {
+ authenticationStep: 'validateUpdate',
+ error: new Parse.Error(
+ Parse.Error.SCRIPT_FAILED,
+ 'You cannot update with that update data.'
+ ),
+ user: user.id,
+ provider: 'modernAdapter',
+ }
+ );
+
+ await reconfigureServer({
+ auth: { modernAdapter: throwInLogin },
+ allowExpiredAuthDataToken: false,
+ });
+ logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ user = new Parse.User();
+ await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } });
+ const user2 = new Parse.User();
+ await expectAsync(
+ user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } })
+ ).toBeRejectedWith(
+ new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.')
+ );
+ expect(logger.error).toHaveBeenCalledWith(
+ `Failed running auth step validateLogin for modernAdapter for user ${user.id} with Error: {"message":"You cannot login with that login data.","code":${Parse.Error.SCRIPT_FAILED}}`,
+ {
+ authenticationStep: 'validateLogin',
+ error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.'),
+ user: user.id,
+ provider: 'modernAdapter',
+ }
+ );
+ });
+
+ it('should return challenge with no logged user', async () => {
+ spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' });
+
+ await reconfigureServer({
+ auth: { challengeAdapter },
+ });
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: {},
+ })
+ ).toBeRejectedWithError('Nothing to challenge.');
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: { challengeData: true },
+ })
+ ).toBeRejectedWithError('challengeData should be an object.');
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: { challengeData: { data: true }, authData: true },
+ })
+ ).toBeRejectedWithError('authData should be an object.');
+
+ const res = await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ }),
+ });
+
+ expect(res.data).toEqual({
+ challengeData: {
+ challengeAdapter: {
+ token: 'test',
+ },
+ },
+ });
+ const challengeCall = challengeAdapter.challenge.calls.argsFor(0);
+ expect(challengeAdapter.challenge).toHaveBeenCalledTimes(1);
+ expect(challengeCall[0]).toEqual({ someData: true });
+ expect(challengeCall[1]).toBeUndefined();
+ expect(challengeCall[2]).toEqual(challengeAdapter);
+ expect(challengeCall[3].master).toBeDefined();
+ expect(challengeCall[3].headers).toBeDefined();
+ expect(challengeCall[3].object).toBeUndefined();
+ expect(challengeCall[3].original).toBeUndefined();
+ expect(challengeCall[3].user).toBeUndefined();
+ expect(challengeCall[3].isChallenge).toBeTruthy();
+ expect(challengeCall.length).toEqual(4);
+ });
+
+ it('should return empty challenge data response if challenged provider does not exists', async () => {
+ spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' });
+
+ await reconfigureServer({
+ auth: { challengeAdapter },
+ });
+
+ const res = await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ nonExistingProvider: { someData: true },
+ },
+ }),
+ });
+
+ expect(res.data).toEqual({ challengeData: {} });
+ });
+ it('should return challenge with username created user', async () => {
+ spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' });
+
+ await reconfigureServer({
+ auth: { challengeAdapter },
+ });
+
+ const user = new Parse.User();
+ await user.save({ username: 'username', password: 'password' });
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ username: 'username',
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('You provided username or email, you need to also provide password.');
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ authData: { data: true },
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ }),
+ })
+ ).toBeRejectedWithError(
+ 'You cannot provide username/email and authData, only use one identification method.'
+ );
+
+ const res = await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ username: 'username',
+ password: 'password',
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ }),
+ });
+
+ expect(res.data).toEqual({
+ challengeData: {
+ challengeAdapter: {
+ token: 'test',
+ },
+ },
+ });
+
+ const challengeCall = challengeAdapter.challenge.calls.argsFor(0);
+ expect(challengeAdapter.challenge).toHaveBeenCalledTimes(1);
+ expect(challengeCall[0]).toEqual({ someData: true });
+ expect(challengeCall[1]).toEqual(undefined);
+ expect(challengeCall[2]).toEqual(challengeAdapter);
+ expect(challengeCall[3].master).toBeDefined();
+ expect(challengeCall[3].isChallenge).toBeTruthy();
+ expect(challengeCall[3].user).toBeUndefined();
+ expect(challengeCall[3].object instanceof Parse.User).toBeTruthy();
+ expect(challengeCall[3].original instanceof Parse.User).toBeTruthy();
+ expect(challengeCall[3].object.id).toEqual(user.id);
+ expect(challengeCall[3].original.id).toEqual(user.id);
+ expect(challengeCall.length).toEqual(4);
+ });
+
+ it('should return challenge with authData created user', async () => {
+ spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' });
+ spyOn(challengeAdapter, 'validateAuthData').and.callThrough();
+
+ await reconfigureServer({
+ auth: { challengeAdapter, soloAdapter },
+ });
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ authData: {
+ challengeAdapter: { id: 'challengeAdapter' },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('User not found.');
+
+ const user = new Parse.User();
+ await user.save({ authData: { challengeAdapter: { id: 'challengeAdapter' } } });
+
+ const user2 = new Parse.User();
+ await user2.save({ authData: { soloAdapter: { id: 'soloAdapter' } } });
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ authData: {
+ challengeAdapter: { id: 'challengeAdapter' },
+ soloAdapter: { id: 'soloAdapter' },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('You cannot provide more than one authData provider with an id.');
+
+ const res = await request({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ authData: {
+ challengeAdapter: { id: 'challengeAdapter' },
+ },
+ }),
+ });
+
+ expect(res.data).toEqual({
+ challengeData: {
+ challengeAdapter: {
+ token: 'test',
+ },
+ },
+ });
+
+ const validateCall = challengeAdapter.validateAuthData.calls.argsFor(1);
+ expect(validateCall[2].isChallenge).toBeTruthy();
+
+ const challengeCall = challengeAdapter.challenge.calls.argsFor(0);
+ expect(challengeAdapter.challenge).toHaveBeenCalledTimes(1);
+ expect(challengeCall[0]).toEqual({ someData: true });
+ expect(challengeCall[1]).toEqual({ id: 'challengeAdapter' });
+ expect(challengeCall[2]).toEqual(challengeAdapter);
+ expect(challengeCall[3].master).toBeDefined();
+ expect(challengeCall[3].isChallenge).toBeTruthy();
+ expect(challengeCall[3].object instanceof Parse.User).toBeTruthy();
+ expect(challengeCall[3].original instanceof Parse.User).toBeTruthy();
+ expect(challengeCall[3].object.id).toEqual(user.id);
+ expect(challengeCall[3].original.id).toEqual(user.id);
+ expect(challengeCall.length).toEqual(4);
+ });
+
+ it('should validate provided authData and prevent guess id attack', async () => {
+ spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' });
+
+ await reconfigureServer({
+ auth: { challengeAdapter, soloAdapter },
+ });
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ authData: {
+ challengeAdapter: { id: 'challengeAdapter' },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('User not found.');
+
+ const user = new Parse.User();
+ await user.save({ authData: { challengeAdapter: { id: 'challengeAdapter' } } });
+
+ spyOn(challengeAdapter, 'validateAuthData').and.rejectWith({});
+
+ await expectAsync(
+ requestWithExpectedError({
+ headers: headers,
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ body: JSON.stringify({
+ challengeData: {
+ challengeAdapter: { someData: true },
+ },
+ authData: {
+ challengeAdapter: { id: 'challengeAdapter' },
+ },
+ }),
+ })
+ ).toBeRejectedWithError('User not found.');
+
+ const validateCall = challengeAdapter.validateAuthData.calls.argsFor(0);
+ expect(challengeAdapter.validateAuthData).toHaveBeenCalledTimes(1);
+ expect(validateCall[0]).toEqual({ id: 'challengeAdapter' });
+ expect(validateCall[1]).toEqual(challengeAdapter);
+ expect(validateCall[2].isChallenge).toBeTruthy();
+ expect(validateCall[2].master).toBeDefined();
+ expect(validateCall[2].object instanceof Parse.User).toBeTruthy();
+ expect(validateCall[2].original instanceof Parse.User).toBeTruthy();
+ expect(validateCall[2].object.id).toEqual(user.id);
+ expect(validateCall[2].original.id).toEqual(user.id);
+ expect(validateCall.length).toEqual(3);
+ });
+
+ it('should work with multiple adapters', async () => {
+ const adapterA = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+ const adapterB = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+ await reconfigureServer({ auth: { adapterA, adapterB } });
+ const user = new Parse.User();
+ await user.signUp({
+ username: 'test',
+ password: 'password',
+ });
+ await user.save({ authData: { adapterA: { id: 'testA' } } });
+ expect(user.get('authData')).toEqual({ adapterA: { id: 'testA' } });
+ await user.save({ authData: { adapterA: null, adapterB: { id: 'test' } } });
+ await user.fetch({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({ adapterB: { id: 'test' } });
+ });
+
+ it('should unlink a code-based auth provider without triggering adapter validation', async () => {
+ const mockUserId = 'gpgamesUser123';
+ const mockAccessToken = 'mockAccessToken';
+
+ const otherAdapter = {
+ validateAppId: () => Promise.resolve(),
+ validateAuthData: () => Promise.resolve(),
+ };
+
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: mockAccessToken }),
+ },
+ },
+ {
+ url: `https://www.googleapis.com/games/v1/players/${mockUserId}`,
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ playerId: mockUserId }),
+ },
+ },
+ ]);
+
+ await reconfigureServer({
+ auth: {
+ gpgames: {
+ clientId: 'testClientId',
+ clientSecret: 'testClientSecret',
+ },
+ otherAdapter,
+ },
+ });
+
+ // Sign up with username/password, then link providers
+ const user = new Parse.User();
+ await user.signUp({ username: 'gpgamesTestUser', password: 'password123' });
+
+ // Link gpgames code-based provider
+ await user.save({
+ authData: {
+ gpgames: { id: mockUserId, code: 'authCode123', redirect_uri: 'https://example.com/callback' },
+ },
+ });
+
+ // Link a second provider
+ await user.save({ authData: { otherAdapter: { id: 'other1' } } });
+
+ // Reset fetch spy to track calls during unlink
+ global.fetch.calls.reset();
+
+ // Unlink gpgames by setting authData to null; should not call beforeFind / external APIs
+ const sessionToken = user.getSessionToken();
+ await user.save({ authData: { gpgames: null } }, { sessionToken });
+
+ // No external HTTP calls should have been made during unlink
+ expect(global.fetch.calls.count()).toBe(0);
+
+ // Verify gpgames was removed while the other provider remains
+ await user.fetch({ useMasterKey: true });
+ const authData = user.get('authData');
+ expect(authData).toBeDefined();
+ expect(authData.gpgames).toBeUndefined();
+ expect(authData.otherAdapter).toEqual({ id: 'other1' });
+ });
+
+ it('should unlink one code-based provider while echoing back another unchanged', async () => {
+ const gpgamesUserId = 'gpgamesUser1';
+ const instagramUserId = 'igUser1';
+
+ // Mock gpgames API for initial login
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'gpgamesToken' }),
+ },
+ },
+ {
+ url: `https://www.googleapis.com/games/v1/players/${gpgamesUserId}`,
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ playerId: gpgamesUserId }),
+ },
+ },
+ ]);
+
+ await reconfigureServer({
+ auth: {
+ gpgames: {
+ clientId: 'testClientId',
+ clientSecret: 'testClientSecret',
+ },
+ instagram: {
+ clientId: 'testClientId',
+ clientSecret: 'testClientSecret',
+ redirectUri: 'https://example.com/callback',
+ },
+ },
+ });
+
+ // Login with gpgames
+ const user = await Parse.User.logInWith('gpgames', {
+ authData: { id: gpgamesUserId, code: 'gpCode1', redirect_uri: 'https://example.com/callback' },
+ });
+ const sessionToken = user.getSessionToken();
+
+ // Mock instagram API for linking
+ mockFetch([
+ {
+ url: 'https://api.instagram.com/oauth/access_token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: 'igToken' }),
+ },
+ },
+ {
+ url: `https://graph.instagram.com/me?fields=id&access_token=igToken`,
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ id: instagramUserId }),
+ },
+ },
+ ]);
+
+ // Link instagram as second provider
+ await user.save(
+ { authData: { instagram: { id: instagramUserId, code: 'igCode1' } } },
+ { sessionToken }
+ );
+
+ // Fetch to get current authData (afterFind strips credentials, leaving only { id })
+ await user.fetch({ sessionToken });
+ const currentAuthData = user.get('authData');
+ expect(currentAuthData.gpgames).toBeDefined();
+ expect(currentAuthData.instagram).toBeDefined();
+
+ // Reset fetch spy
+ global.fetch.calls.reset();
+
+ // Unlink gpgames while echoing back instagram unchanged â the common client pattern:
+ // fetch current state, spread it, set the one to unlink to null
+ user.set('authData', { ...currentAuthData, gpgames: null });
+ await user.save(null, { sessionToken });
+
+ // No external HTTP calls during unlink (no code exchange for unchanged instagram)
+ expect(global.fetch.calls.count()).toBe(0);
+
+ // Verify gpgames removed, instagram preserved
+ await user.fetch({ useMasterKey: true });
+ const finalAuthData = user.get('authData');
+ expect(finalAuthData).toBeDefined();
+ expect(finalAuthData.gpgames).toBeUndefined();
+ expect(finalAuthData.instagram).toBeDefined();
+ expect(finalAuthData.instagram.id).toBe(instagramUserId);
+ });
+
+ it('should reject changing an existing code-based provider id without credentials', async () => {
+ const mockUserId = 'gpgamesUser123';
+ const mockAccessToken = 'mockAccessToken';
+
+ mockFetch([
+ {
+ url: 'https://oauth2.googleapis.com/token',
+ method: 'POST',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ access_token: mockAccessToken }),
+ },
+ },
+ {
+ url: `https://www.googleapis.com/games/v1/players/${mockUserId}`,
+ method: 'GET',
+ response: {
+ ok: true,
+ json: () => Promise.resolve({ playerId: mockUserId }),
+ },
+ },
+ ]);
+
+ await reconfigureServer({
+ auth: {
+ gpgames: {
+ clientId: 'testClientId',
+ clientSecret: 'testClientSecret',
+ },
+ },
+ });
+
+ // Sign up and link gpgames with valid credentials
+ const user = new Parse.User();
+ await user.save({
+ authData: {
+ gpgames: { id: mockUserId, code: 'authCode123', redirect_uri: 'https://example.com/callback' },
+ },
+ });
+ const sessionToken = user.getSessionToken();
+
+ // Attempt to change gpgames id without credentials (no code or access_token)
+ await expectAsync(
+ user.save({ authData: { gpgames: { id: 'differentUserId' } } }, { sessionToken })
+ ).toBeRejectedWith(
+ jasmine.objectContaining({ message: jasmine.stringContaining('code is required') })
+ );
+ });
+
+ it('should reject linking a new code-based provider with only an id and no credentials', async () => {
+ await reconfigureServer({
+ auth: {
+ gpgames: {
+ clientId: 'testClientId',
+ clientSecret: 'testClientSecret',
+ },
+ },
+ });
+
+ // Sign up with username/password (no gpgames linked)
+ const user = new Parse.User();
+ await user.signUp({ username: 'linkTestUser', password: 'password123' });
+ const sessionToken = user.getSessionToken();
+
+ // Attempt to link gpgames with only { id } â no code or access_token
+ await expectAsync(
+ user.save({ authData: { gpgames: { id: 'victimUserId' } } }, { sessionToken })
+ ).toBeRejectedWith(
+ jasmine.objectContaining({ message: jasmine.stringContaining('code is required') })
+ );
+ });
+
+ it('should handle multiple providers: add one while another remains unchanged (code-based)', async () => {
+ await reconfigureServer({
+ auth: {
+ codeBasedAdapter,
+ simpleAdapter,
+ },
+ });
+
+ // Login with code-based provider
+ const user = new Parse.User();
+ await user.save({ authData: { codeBasedAdapter: { id: 'user1', code: 'code1' } } });
+ const sessionToken = user.getSessionToken();
+ await user.fetch({ sessionToken });
+
+ // At this point, authData.codeBasedAdapter only has {id: 'user1'} due to afterFind
+ const current = user.get('authData') || {};
+ expect(current.codeBasedAdapter).toEqual({ id: 'user1' });
+
+ // Add a second provider while keeping the first unchanged
+ user.set('authData', {
+ ...current,
+ simpleAdapter: { id: 'simple1' },
+ // codeBasedAdapter is NOT modified (no new code provided)
+ });
+
+ // This should succeed without requiring 'code' for codeBasedAdapter
+ await user.save(null, { sessionToken });
+
+ // Verify both providers are present
+ const reloaded = await new Parse.Query(Parse.User).get(user.id, {
+ useMasterKey: true,
+ });
+
+ const authData = reloaded.get('authData') || {};
+ expect(authData.simpleAdapter && authData.simpleAdapter.id).toBe('simple1');
+ expect(authData.codeBasedAdapter && authData.codeBasedAdapter.id).toBe('user1');
+ });
+
+ describe('authData dot-notation injection and login crash', () => {
+ it('rejects dotted update key that targets authData sub-field', async () => {
+ const user = new Parse.User();
+ user.setUsername('dotuser');
+ user.setPassword('pass1234');
+ await user.signUp();
+
+ const res = await request({
+ method: 'PUT',
+ url: `http://localhost:8378/1/users/${user.id}`,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ body: JSON.stringify({ 'authData.anonymous".id': 'injected' }),
+ }).catch(e => e);
+ expect(res.status).toBe(400);
+ });
+
+ it('login does not crash when stored authData has unknown provider', async () => {
+ const user = new Parse.User();
+ user.setUsername('dotuser2');
+ user.setPassword('pass1234');
+ await user.signUp();
+ await Parse.User.logOut();
+
+ // Inject unknown provider directly in database to simulate corrupted data
+ const config = Config.get('test');
+ await config.database.update(
+ '_User',
+ { objectId: user.id },
+ { authData: { unknown_provider: { id: 'bad' } } }
+ );
+
+ // Login should not crash with 500
+ const login = await request({
+ method: 'GET',
+ url: `http://localhost:8378/1/login?username=dotuser2&password=pass1234`,
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ }).catch(e => e);
+ expect(login.status).toBe(200);
+ expect(login.data.sessionToken).toBeDefined();
+ });
+ });
+
+ describe('challenge endpoint authData provider value validation', () => {
+ it('rejects challenge request with null provider value without 500', async () => {
+ const res = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: JSON.stringify({
+ authData: { anonymous: null },
+ challengeData: { anonymous: { token: '123456' } },
+ }),
+ }).catch(e => e);
+ expect(res.status).toBeGreaterThanOrEqual(400);
+ expect(res.status).toBeLessThan(500);
+ });
+
+ it('rejects challenge request with non-object provider value without 500', async () => {
+ const res = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/challenge',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: JSON.stringify({
+ authData: { anonymous: 'string_value' },
+ challengeData: { anonymous: { token: '123456' } },
+ }),
+ }).catch(e => e);
+ expect(res.status).toBeGreaterThanOrEqual(400);
+ expect(res.status).toBeLessThan(500);
+ });
+ });
+});
diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js
index 733e1e3a00..39061b8083 100644
--- a/spec/CLI.spec.js
+++ b/spec/CLI.spec.js
@@ -1,30 +1,40 @@
'use strict';
-var commander = require("../src/cli/utils/commander").default;
+let commander;
+const definitions = require('../lib/cli/definitions/parse-server').default;
+const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server').default;
+const path = require('path');
+const { spawn } = require('child_process');
-var definitions = {
- "arg0": "PROGRAM_ARG_0",
- "arg1": {
- env: "PROGRAM_ARG_1",
- required: true
+const testDefinitions = {
+ arg0: 'PROGRAM_ARG_0',
+ arg1: {
+ env: 'PROGRAM_ARG_1',
+ required: true,
},
- "arg2": {
- env: "PROGRAM_ARG_2",
- action: function(value) {
- var value = parseInt(value);
- if (!Number.isInteger(value)) {
- throw "arg2 is invalid";
+ arg2: {
+ env: 'PROGRAM_ARG_2',
+ action: function (value) {
+ const intValue = parseInt(value);
+ if (!Number.isInteger(intValue)) {
+ throw 'arg2 is invalid';
}
- return value;
- }
+ return intValue;
+ },
},
- "arg3": {},
- "arg4": {
- default: "arg4Value"
- }
-}
+ arg3: {},
+ arg4: {
+ default: 'arg4Value',
+ },
+};
-describe("commander additions", () => {
- afterEach((done) => {
+describe('commander additions', () => {
+ beforeEach(() => {
+ const command = require('../lib/cli/utils/commander').default;
+ commander = new command.constructor();
+ commander.storeOptionsAsProperties();
+ commander.allowExcessArguments();
+ });
+ afterEach(done => {
commander.options = [];
delete commander.arg0;
delete commander.arg1;
@@ -32,107 +42,267 @@ describe("commander additions", () => {
delete commander.arg3;
delete commander.arg4;
done();
- })
+ });
- it("should load properly definitions from args", (done) => {
- commander.loadDefinitions(definitions);
- commander.parse(["node","./CLI.spec.js","--arg0", "arg0Value", "--arg1", "arg1Value", "--arg2", "2", "--arg3", "some"]);
- expect(commander.arg0).toEqual("arg0Value");
- expect(commander.arg1).toEqual("arg1Value");
+ it('should load properly definitions from args', done => {
+ commander.loadDefinitions(testDefinitions);
+ commander.parse([
+ 'node',
+ './CLI.spec.js',
+ '--arg0',
+ 'arg0Value',
+ '--arg1',
+ 'arg1Value',
+ '--arg2',
+ '2',
+ '--arg3',
+ 'some',
+ ]);
+ expect(commander.arg0).toEqual('arg0Value');
+ expect(commander.arg1).toEqual('arg1Value');
expect(commander.arg2).toEqual(2);
- expect(commander.arg3).toEqual("some");
- expect(commander.arg4).toEqual("arg4Value");
+ expect(commander.arg3).toEqual('some');
+ expect(commander.arg4).toEqual('arg4Value');
done();
});
- it("should load properly definitions from env", (done) => {
- commander.loadDefinitions(definitions);
+ it('should load properly definitions from env', done => {
+ commander.loadDefinitions(testDefinitions);
commander.parse([], {
- "PROGRAM_ARG_0": "arg0ENVValue",
- "PROGRAM_ARG_1": "arg1ENVValue",
- "PROGRAM_ARG_2": "3",
+ PROGRAM_ARG_0: 'arg0ENVValue',
+ PROGRAM_ARG_1: 'arg1ENVValue',
+ PROGRAM_ARG_2: '3',
});
- expect(commander.arg0).toEqual("arg0ENVValue");
- expect(commander.arg1).toEqual("arg1ENVValue");
+ expect(commander.arg0).toEqual('arg0ENVValue');
+ expect(commander.arg1).toEqual('arg1ENVValue');
expect(commander.arg2).toEqual(3);
- expect(commander.arg4).toEqual("arg4Value");
+ expect(commander.arg4).toEqual('arg4Value');
done();
});
- it("should load properly use args over env", (done) => {
- commander.loadDefinitions(definitions);
- commander.parse(["node","./CLI.spec.js","--arg0", "arg0Value", "--arg4", "anotherArg4"], {
- "PROGRAM_ARG_0": "arg0ENVValue",
- "PROGRAM_ARG_1": "arg1ENVValue",
- "PROGRAM_ARG_2": "4",
+ it('should load properly use args over env', () => {
+ commander.loadDefinitions(testDefinitions);
+ commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''], {
+ PROGRAM_ARG_0: 'arg0ENVValue',
+ PROGRAM_ARG_1: 'arg1ENVValue',
+ PROGRAM_ARG_2: '4',
+ PROGRAM_ARG_4: 'arg4ENVValue',
});
- expect(commander.arg0).toEqual("arg0Value");
- expect(commander.arg1).toEqual("arg1ENVValue");
+ expect(commander.arg0).toEqual('arg0Value');
+ expect(commander.arg1).toEqual('arg1ENVValue');
expect(commander.arg2).toEqual(4);
- expect(commander.arg4).toEqual("anotherArg4");
- done();
+ expect(commander.arg4).toEqual('');
});
- it("should fail in action as port is invalid", (done) => {
- commander.loadDefinitions(definitions);
- expect(()=> {
- commander.parse(["node","./CLI.spec.js","--arg0", "arg0Value"], {
- "PROGRAM_ARG_0": "arg0ENVValue",
- "PROGRAM_ARG_1": "arg1ENVValue",
- "PROGRAM_ARG_2": "hello",
+ it('should fail in action as port is invalid', done => {
+ commander.loadDefinitions(testDefinitions);
+ expect(() => {
+ commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value'], {
+ PROGRAM_ARG_0: 'arg0ENVValue',
+ PROGRAM_ARG_1: 'arg1ENVValue',
+ PROGRAM_ARG_2: 'hello',
});
- }).toThrow("arg2 is invalid");
+ }).toThrow('arg2 is invalid');
done();
});
- it("should not override config.json", (done) => {
- commander.loadDefinitions(definitions);
- commander.parse(["node","./CLI.spec.js","--arg0", "arg0Value", "./spec/configs/CLIConfig.json"], {
- "PROGRAM_ARG_0": "arg0ENVValue",
- "PROGRAM_ARG_1": "arg1ENVValue",
- });
- let options = commander.getOptions();
+ it('should not override config.json', done => {
+ spyOn(console, 'log').and.callFake(() => {});
+ commander.loadDefinitions(testDefinitions);
+ commander.parse(
+ ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfig.json'],
+ {
+ PROGRAM_ARG_0: 'arg0ENVValue',
+ PROGRAM_ARG_1: 'arg1ENVValue',
+ }
+ );
+ const options = commander.getOptions();
expect(options.arg2).toBe(8888);
- expect(options.arg3).toBe("hello"); //config value
+ expect(options.arg3).toBe('hello'); //config value
expect(options.arg4).toBe('/1');
done();
});
- it("should fail with invalid values in JSON", (done) => {
- commander.loadDefinitions(definitions);
+ it('should fail with invalid values in JSON', done => {
+ commander.loadDefinitions(testDefinitions);
expect(() => {
- commander.parse(["node","./CLI.spec.js","--arg0", "arg0Value", "./spec/configs/CLIConfigFail.json"], {
- "PROGRAM_ARG_0": "arg0ENVValue",
- "PROGRAM_ARG_1": "arg1ENVValue",
- });
- }).toThrow("arg2 is invalid")
+ commander.parse(
+ ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfigFail.json'],
+ {
+ PROGRAM_ARG_0: 'arg0ENVValue',
+ PROGRAM_ARG_1: 'arg1ENVValue',
+ }
+ );
+ }).toThrow('arg2 is invalid');
done();
});
- it("should fail when too many apps are set", (done) => {
- commander.loadDefinitions(definitions);
+ it('should fail when too many apps are set', done => {
+ commander.loadDefinitions(testDefinitions);
expect(() => {
- commander.parse(["node","./CLI.spec.js","./spec/configs/CLIConfigFailTooManyApps.json"]);
- }).toThrow("Multiple apps are not supported")
+ commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigFailTooManyApps.json']);
+ }).toThrow('Multiple apps are not supported');
done();
});
- it("should load config from apps", (done) => {
- commander.loadDefinitions(definitions);
- commander.parse(["node", "./CLI.spec.js", "./spec/configs/CLIConfigApps.json"]);
- let options = commander.getOptions();
- expect(options.arg1).toBe("my_app");
+ it('should load config from apps', done => {
+ spyOn(console, 'log').and.callFake(() => {});
+ commander.loadDefinitions(testDefinitions);
+ commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigApps.json']);
+ const options = commander.getOptions();
+ expect(options.arg1).toBe('my_app');
expect(options.arg2).toBe(8888);
- expect(options.arg3).toBe("hello"); //config value
+ expect(options.arg3).toBe('hello'); //config value
expect(options.arg4).toBe('/1');
done();
});
- it("should fail when passing an invalid arguement", (done) => {
- commander.loadDefinitions(definitions);
+ it('should fail when passing an invalid arguement', done => {
+ commander.loadDefinitions(testDefinitions);
expect(() => {
- commander.parse(["node", "./CLI.spec.js", "./spec/configs/CLIConfigUnknownArg.json"]);
- }).toThrow('error: unknown option myArg')
+ commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigUnknownArg.json']);
+ }).toThrow('error: unknown option myArg');
done();
});
});
+
+describe('definitions', () => {
+ it('should have valid types', () => {
+ for (const key in definitions) {
+ const definition = definitions[key];
+ expect(typeof definition).toBe('object');
+ if (typeof definition.env !== 'undefined') {
+ expect(typeof definition.env).toBe('string');
+ }
+ expect(typeof definition.help).toBe('string');
+ if (typeof definition.required !== 'undefined') {
+ expect(typeof definition.required).toBe('boolean');
+ }
+ if (typeof definition.action !== 'undefined') {
+ expect(typeof definition.action).toBe('function');
+ }
+ }
+ });
+
+ it('should throw when using deprecated facebookAppIds', () => {
+ expect(() => {
+ definitions.facebookAppIds.action();
+ }).toThrow();
+ });
+});
+
+describe('LiveQuery definitions', () => {
+ it('should have valid types', () => {
+ for (const key in liveQueryDefinitions) {
+ const definition = liveQueryDefinitions[key];
+ expect(typeof definition).toBe('object');
+ if (typeof definition.env !== 'undefined') {
+ expect(typeof definition.env).toBe('string');
+ }
+ expect(typeof definition.help).toBe('string', `help for ${key} should be a string`);
+ if (typeof definition.required !== 'undefined') {
+ expect(typeof definition.required).toBe('boolean');
+ }
+ if (typeof definition.action !== 'undefined') {
+ expect(typeof definition.action).toBe('function');
+ }
+ }
+ });
+});
+
+describe('execution', () => {
+ const binPath = path.resolve(__dirname, '../bin/parse-server');
+ let childProcess;
+
+ function waitForStartup(cp, requiredOutput) {
+ return new Promise((resolve, reject) => {
+ const aggregated = [];
+ cp.stdout.on('data', data => {
+ aggregated.push(data.toString());
+ if (requiredOutput.every(r => aggregated.some(a => a.includes(r)))) {
+ resolve();
+ }
+ });
+ cp.on('error', reject);
+ });
+ }
+
+ afterEach(done => {
+ if (childProcess) {
+ childProcess.on('close', () => {
+ childProcess = undefined;
+ done();
+ });
+ childProcess.kill();
+ }
+ });
+
+ it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)('should start Parse Server', async () => {
+ const env = { ...process.env };
+ env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
+ childProcess = spawn(
+ binPath,
+ ['--appId', 'test', '--masterKey', 'test', '--databaseURI', databaseURI, '--port', '1339'],
+ { env }
+ );
+ await waitForStartup(childProcess, ['parse-server running on']);
+ });
+
+ it_id('d7165081-b133-4cba-901b-19128ce41301')(it)('should start Parse Server with GraphQL', async () => {
+ const env = { ...process.env };
+ env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
+ childProcess = spawn(
+ binPath,
+ [
+ '--appId',
+ 'test',
+ '--masterKey',
+ 'test',
+ '--databaseURI',
+ databaseURI,
+ '--port',
+ '1340',
+ '--mountGraphQL',
+ ],
+ { env }
+ );
+ await waitForStartup(childProcess, ['parse-server running on', 'GraphQL running on']);
+ });
+
+ it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)('should start Parse Server with GraphQL and Playground', async () => {
+ const env = { ...process.env };
+ env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
+ childProcess = spawn(
+ binPath,
+ [
+ '--appId',
+ 'test',
+ '--masterKey',
+ 'test',
+ '--databaseURI',
+ databaseURI,
+ '--port',
+ '1341',
+ '--mountGraphQL',
+ '--mountPlayground',
+ ],
+ { env }
+ );
+ await waitForStartup(childProcess, [
+ 'parse-server running on',
+ 'Playground running on',
+ 'GraphQL running on',
+ ]);
+ });
+
+ it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)('can start Parse Server with auth via CLI', async () => {
+ const env = { ...process.env };
+ env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
+ childProcess = spawn(
+ binPath,
+ ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'],
+ { env }
+ );
+ await waitForStartup(childProcess, ['parse-server running on']);
+ });
+});
diff --git a/spec/CacheController.spec.js b/spec/CacheController.spec.js
index 1e02d59e2f..de07126214 100644
--- a/spec/CacheController.spec.js
+++ b/spec/CacheController.spec.js
@@ -1,24 +1,23 @@
-var CacheController = require('../src/Controllers/CacheController.js').default;
+const CacheController = require('../lib/Controllers/CacheController.js').default;
-describe('CacheController', function() {
- var FakeCacheAdapter;
- var FakeAppID = 'foo';
- var KEY = 'hello';
+describe('CacheController', function () {
+ let FakeCacheAdapter;
+ const FakeAppID = 'foo';
+ const KEY = 'hello';
beforeEach(() => {
FakeCacheAdapter = {
get: () => Promise.resolve(null),
put: jasmine.createSpy('put'),
del: jasmine.createSpy('del'),
- clear: jasmine.createSpy('clear')
- }
+ clear: jasmine.createSpy('clear'),
+ };
spyOn(FakeCacheAdapter, 'get').and.callThrough();
});
-
- it('should expose role and user caches', (done) => {
- var cache = new CacheController(FakeCacheAdapter, FakeAppID);
+ it('should expose role and user caches', done => {
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID);
expect(cache.role).not.toEqual(null);
expect(cache.role.get).not.toEqual(null);
@@ -28,27 +27,26 @@ describe('CacheController', function() {
done();
});
-
- ['role', 'user'].forEach((cacheName) => {
+ ['role', 'user'].forEach(cacheName => {
it('should prefix ' + cacheName + ' cache', () => {
- var cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName];
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName];
cache.put(KEY, 'world');
- var firstPut = FakeCacheAdapter.put.calls.first();
+ const firstPut = FakeCacheAdapter.put.calls.first();
expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
cache.get(KEY);
- var firstGet = FakeCacheAdapter.get.calls.first();
+ const firstGet = FakeCacheAdapter.get.calls.first();
expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
cache.del(KEY);
- var firstDel = FakeCacheAdapter.del.calls.first();
+ const firstDel = FakeCacheAdapter.del.calls.first();
expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
});
});
it('should clear the entire cache', () => {
- var cache = new CacheController(FakeCacheAdapter, FakeAppID);
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID);
cache.clear();
expect(FakeCacheAdapter.clear.calls.count()).toEqual(1);
@@ -60,15 +58,13 @@ describe('CacheController', function() {
expect(FakeCacheAdapter.clear.calls.count()).toEqual(3);
});
- it('should handle cache rejections', (done) => {
-
- FakeCacheAdapter.get = () => Promise.reject();
+ it('should handle cache rejections', done => {
+ FakeCacheAdapter.get = () => Promise.reject();
- var cache = new CacheController(FakeCacheAdapter, FakeAppID);
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID);
- cache.get('foo').then(done, () => {
- fail('Promise should not be rejected.');
- });
+ cache.get('foo').then(done, () => {
+ fail('Promise should not be rejected.');
+ });
});
-
});
diff --git a/spec/Client.spec.js b/spec/Client.spec.js
index 14b1795529..0de226204a 100644
--- a/spec/Client.spec.js
+++ b/spec/Client.spec.js
@@ -1,121 +1,120 @@
-var Client = require('../src/LiveQuery/Client').Client;
-var ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
+const Client = require('../lib/LiveQuery/Client').Client;
+const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket;
-describe('Client', function() {
- it('can be initialized', function() {
- var parseWebSocket = new ParseWebSocket({});
- var client = new Client(1, parseWebSocket);
+describe('Client', function () {
+ it('can be initialized', function () {
+ const parseWebSocket = new ParseWebSocket({});
+ const client = new Client(1, parseWebSocket);
expect(client.id).toBe(1);
expect(client.parseWebSocket).toBe(parseWebSocket);
expect(client.subscriptionInfos.size).toBe(0);
});
- it('can push response', function() {
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ it('can push response', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
Client.pushResponse(parseWebSocket, 'message');
expect(parseWebSocket.send).toHaveBeenCalledWith('message');
});
- it('can push error', function() {
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ it('can push error', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
Client.pushError(parseWebSocket, 1, 'error', true);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('error');
expect(messageJSON.error).toBe('error');
expect(messageJSON.code).toBe(1);
expect(messageJSON.reconnect).toBe(true);
});
- it('can add subscription information', function() {
- var subscription = {};
- var fields = ['test'];
- var subscriptionInfo = {
+ it('can add subscription information', function () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
subscription: subscription,
- fields: fields
- }
- var client = new Client(1, {});
+ fields: fields,
+ };
+ const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
expect(client.subscriptionInfos.size).toBe(1);
expect(client.subscriptionInfos.get(1)).toBe(subscriptionInfo);
});
- it('can get subscription information', function() {
- var subscription = {};
- var fields = ['test'];
- var subscriptionInfo = {
+ it('can get subscription information', function () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
subscription: subscription,
- fields: fields
- }
- var client = new Client(1, {});
+ fields: fields,
+ };
+ const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
- var subscriptionInfoAgain = client.getSubscriptionInfo(1);
+ const subscriptionInfoAgain = client.getSubscriptionInfo(1);
expect(subscriptionInfoAgain).toBe(subscriptionInfo);
});
- it('can delete subscription information', function() {
- var subscription = {};
- var fields = ['test'];
- var subscriptionInfo = {
+ it('can delete subscription information', function () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
subscription: subscription,
- fields: fields
- }
- var client = new Client(1, {});
+ fields: fields,
+ };
+ const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
client.deleteSubscriptionInfo(1);
expect(client.subscriptionInfos.size).toBe(0);
});
-
- it('can generate ParseObject JSON with null selected field', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can generate ParseObject JSON with null selected field', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
};
- var client = new Client(1, {});
+ const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON);
});
- it('can generate ParseObject JSON with undefined selected field', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can generate ParseObject JSON with undefined selected field', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
};
- var client = new Client(1, {});
+ const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON);
});
- it('can generate ParseObject JSON with selected fields', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can generate ParseObject JSON with selected fields', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var client = new Client(1, {});
+ const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({
className: 'test',
@@ -123,22 +122,22 @@ describe('Client', function() {
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
});
});
- it('can generate ParseObject JSON with nonexistent selected fields', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can generate ParseObject JSON with nonexistent selected fields', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var client = new Client(1, {});
- var limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']);
+ const client = new Client(1, {});
+ const limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']);
expect(limitedParseObject).toEqual({
className: 'test',
@@ -150,137 +149,137 @@ describe('Client', function() {
expect('name' in limitedParseObject).toBe(false);
});
- it('can push connect response', function() {
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ it('can push connect response', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushConnect();
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('connected');
expect(messageJSON.clientId).toBe(1);
});
- it('can push subscribe response', function() {
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ it('can push subscribe response', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushSubscribe(2);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('subscribed');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
});
- it('can push unsubscribe response', function() {
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ it('can push unsubscribe response', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushUnsubscribe(2);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('unsubscribed');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
});
- it('can push create response', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can push create response', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushCreate(2, parseObjectJSON);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('create');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
expect(messageJSON.object).toEqual(parseObjectJSON);
});
- it('can push enter response', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can push enter response', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushEnter(2, parseObjectJSON);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('enter');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
expect(messageJSON.object).toEqual(parseObjectJSON);
});
- it('can push update response', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can push update response', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushUpdate(2, parseObjectJSON);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('update');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
expect(messageJSON.object).toEqual(parseObjectJSON);
});
- it('can push leave response', function() {
- var parseObjectJSON = {
- key : 'value',
+ it('can push leave response', function () {
+ const parseObjectJSON = {
+ key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
- test: 'test'
+ test: 'test',
};
- var parseWebSocket = {
- send: jasmine.createSpy('send')
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
};
- var client = new Client(1, parseWebSocket);
+ const client = new Client(1, parseWebSocket);
client.pushLeave(2, parseObjectJSON);
- var lastCall = parseWebSocket.send.calls.first();
- var messageJSON = JSON.parse(lastCall.args[0]);
+ const lastCall = parseWebSocket.send.calls.first();
+ const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('leave');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
diff --git a/spec/ClientSDK.spec.js b/spec/ClientSDK.spec.js
index e714818159..987770833c 100644
--- a/spec/ClientSDK.spec.js
+++ b/spec/ClientSDK.spec.js
@@ -1,41 +1,49 @@
-var ClientSDK = require('../src/ClientSDK');
+const ClientSDK = require('../lib/ClientSDK');
-describe('ClientSDK', () =>Â {
- it('should properly parse the SDK versions', () =>Â {
- let clientSDKFromVersion = ClientSDK.fromString;
- expect(clientSDKFromVersion('i1.1.1')).toEqual({
- sdk: 'i',
- version: '1.1.1'
- });
- expect(clientSDKFromVersion('i1')).toEqual({
- sdk: 'i',
- version: '1'
- });
- expect(clientSDKFromVersion('apple-tv1.13.0')).toEqual({
- sdk: 'apple-tv',
- version: '1.13.0'
- });
- expect(clientSDKFromVersion('js1.9.0')).toEqual({
- sdk: 'js',
- version: '1.9.0'
- });
+describe('ClientSDK', () => {
+ it('should properly parse the SDK versions', () => {
+ const clientSDKFromVersion = ClientSDK.fromString;
+ expect(clientSDKFromVersion('i1.1.1')).toEqual({
+ sdk: 'i',
+ version: '1.1.1',
+ });
+ expect(clientSDKFromVersion('i1')).toEqual({
+ sdk: 'i',
+ version: '1',
+ });
+ expect(clientSDKFromVersion('apple-tv1.13.0')).toEqual({
+ sdk: 'apple-tv',
+ version: '1.13.0',
+ });
+ expect(clientSDKFromVersion('js1.9.0')).toEqual({
+ sdk: 'js',
+ version: '1.9.0',
+ });
});
-
- it('should properly sastisfy', () =>Â {
- expect(ClientSDK.compatible({
- js: '>=1.9.0'
- })("js1.9.0")).toBe(true);
-
- expect(ClientSDK.compatible({
- js: '>=1.9.0'
- })("js2.0.0")).toBe(true);
- expect(ClientSDK.compatible({
- js: '>=1.9.0'
- })("js1.8.0")).toBe(false);
+ it('should properly sastisfy', () => {
+ expect(
+ ClientSDK.compatible({
+ js: '>=1.9.0',
+ })('js1.9.0')
+ ).toBe(true);
- expect(ClientSDK.compatible({
- js: '>=1.9.0'
- })(undefined)).toBe(true);
- })
-})
\ No newline at end of file
+ expect(
+ ClientSDK.compatible({
+ js: '>=1.9.0',
+ })('js2.0.0')
+ ).toBe(true);
+
+ expect(
+ ClientSDK.compatible({
+ js: '>=1.9.0',
+ })('js1.8.0')
+ ).toBe(false);
+
+ expect(
+ ClientSDK.compatible({
+ js: '>=1.9.0',
+ })(undefined)
+ ).toBe(true);
+ });
+});
diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js
new file mode 100644
index 0000000000..11ccc82766
--- /dev/null
+++ b/spec/CloudCode.Validator.spec.js
@@ -0,0 +1,1811 @@
+'use strict';
+const Parse = require('parse/node');
+const validatorFail = () => {
+ throw 'you are not authorized';
+};
+const validatorSuccess = () => {
+ return true;
+};
+function testConfig() {
+ return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
+}
+
+describe('cloud validator', () => {
+ it('complete validator', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ () => {}
+ );
+ try {
+ const result = await Parse.Cloud.run('myFunction', {});
+ expect(result).toBe('myFunc');
+ done();
+ } catch (e) {
+ fail('should not have thrown error');
+ }
+ });
+
+ it('Throw from validator', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ () => {
+ throw 'error';
+ }
+ );
+ try {
+ await Parse.Cloud.run('myFunction');
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validator can throw parse error', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
+ }
+ );
+ try {
+ await Parse.Cloud.run('myFunction');
+ fail('should have validation error');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBe('It should fail');
+ done();
+ }
+ });
+
+ it('validator can throw parse error with no message', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED);
+ }
+ );
+ try {
+ await Parse.Cloud.run('myFunction');
+ fail('should have validation error');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBeUndefined();
+ done();
+ }
+ });
+
+ it('async validator', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ async () => {
+ await new Promise(resolve => {
+ setTimeout(resolve, 1000);
+ });
+ throw 'async error';
+ }
+ );
+ try {
+ await Parse.Cloud.run('myFunction');
+ fail('should have validation error');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ expect(e.message).toBe('async error');
+ done();
+ }
+ });
+
+ it('pass function to validator', async done => {
+ const validator = request => {
+ expect(request).toBeDefined();
+ expect(request.params).toBeDefined();
+ expect(request.master).toBe(false);
+ expect(request.user).toBeUndefined();
+ expect(request.installationId).toBeDefined();
+ expect(request.log).toBeDefined();
+ expect(request.headers).toBeDefined();
+ expect(request.functionName).toBeDefined();
+ expect(request.context).toBeDefined();
+ done();
+ };
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {
+ return 'myFunc';
+ },
+ validator
+ );
+ await Parse.Cloud.run('myFunction');
+ });
+
+ it('require user on cloud functions', async done => {
+ Parse.Cloud.define(
+ 'hello1',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ requireUser: true,
+ }
+ );
+ try {
+ await Parse.Cloud.run('hello1', {});
+ fail('function should have failed.');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please login to continue.');
+ done();
+ }
+ });
+
+ it('require master on cloud functions', done => {
+ Parse.Cloud.define(
+ 'hello2',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ requireMaster: true,
+ }
+ );
+ Parse.Cloud.run('hello2', {})
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual(
+ 'Validation failed. Master key is required to complete this request.'
+ );
+ done();
+ });
+ });
+
+ it('set params on cloud functions', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: ['a'],
+ }
+ );
+ Parse.Cloud.run('hello', {})
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please specify data for a.');
+ done();
+ });
+ });
+
+ it('allow params on cloud functions', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.a).toEqual('yolo');
+ return 'Hello world!';
+ },
+ {
+ fields: ['a'],
+ }
+ );
+ Parse.Cloud.run('hello', { a: 'yolo' })
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ fail('Error should not have been called.');
+ });
+ });
+
+ it('set params type array', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: Array,
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: '' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: array');
+ done();
+ });
+ });
+
+ it('set params type allow array', async () => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: Array,
+ },
+ },
+ }
+ );
+ const result = await Parse.Cloud.run('hello', { data: [{ foo: 'bar' }] });
+ expect(result).toBe('Hello world!');
+ });
+
+ it('set params type', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: [] })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string');
+ done();
+ });
+ });
+
+ it('set params default', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe('yolo');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ default: 'yolo',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello')
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ fail('function should not have failed.');
+ });
+ });
+
+ it('set params required', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe('yolo');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: true,
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', {})
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please specify data for data.');
+ done();
+ });
+ });
+
+ it('set params not-required options data', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe('abc');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: false,
+ options: s => {
+ return s.length >= 4 && s.length <= 50;
+ },
+ error: 'Validation failed. Expected length of data to be between 4 and 50.',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'abc' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual(
+ 'Validation failed. Expected length of data to be between 4 and 50.'
+ );
+ done();
+ });
+ });
+
+ it('set params not-required type', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe(null);
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: false,
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: null })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string');
+ done();
+ });
+ });
+
+ it('set params not-required options', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: false,
+ options: s => {
+ return s.length >= 4 && s.length <= 50;
+ },
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', {})
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ fail('function should not have failed.');
+ });
+ });
+
+ it('set params not-required no-options', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: false,
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', {})
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ fail('function should not have failed.');
+ });
+ });
+
+ it('set params option', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe('yolo');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: true,
+ options: 'a',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'f' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a');
+ done();
+ });
+ });
+
+ it('set params options', done => {
+ Parse.Cloud.define(
+ 'hello',
+ req => {
+ expect(req.params.data).toBe('yolo');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: true,
+ options: ['a', 'b'],
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'f' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a, b');
+ done();
+ });
+ });
+
+ it('set params options function', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ fail('cloud function should not run.');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: Number,
+ required: true,
+ options: val => {
+ return val > 1 && val < 5;
+ },
+ error: 'Validation failed. Expected data to be between 1 and 5.',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 7 })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Expected data to be between 1 and 5.');
+ done();
+ });
+ });
+
+ it('can run params function on null', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ fail('cloud function should not run.');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ options: val => {
+ return val.length > 5;
+ },
+ error: 'Validation failed. String should be at least 5 characters',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: null })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. String should be at least 5 characters');
+ done();
+ });
+ });
+
+ it('can throw from options validator', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ fail('cloud function should not run.');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ options: () => {
+ throw 'validation failed.';
+ },
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'a' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('validation failed.');
+ done();
+ });
+ });
+
+ it('can throw null from options validator', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ fail('cloud function should not run.');
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ options: () => {
+ throw null;
+ },
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'a' })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Invalid value for data.');
+ done();
+ });
+ });
+
+ it('can create functions', done => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ requireUser: false,
+ requireMaster: false,
+ fields: {
+ data: {
+ type: String,
+ },
+ data1: {
+ type: String,
+ default: 'default',
+ },
+ },
+ }
+ );
+ Parse.Cloud.run('hello', { data: 'str' }).then(result => {
+ expect(result).toEqual('Hello world!');
+ done();
+ });
+ });
+
+ it('basic beforeSave requireUserKey', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUser: true,
+ requireUserKeys: ['name'],
+ });
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ user.set('name', 'foo');
+ await user.save(null, { sessionToken: user.getSessionToken() });
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+ expect(obj.get('foo')).toBe('bar');
+ done();
+ });
+
+ it('basic beforeSave skipWithMasterKey', async function (done) {
+ Parse.Cloud.beforeSave(
+ 'BeforeSave',
+ () => {
+ throw 'before save should have resolved using masterKey.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const obj = new Parse.Object('BeforeSave');
+ obj.set('foo', 'bar');
+ await obj.save(null, { useMasterKey: true });
+ expect(obj.get('foo')).toBe('bar');
+ done();
+ });
+
+ it('basic beforeFind skipWithMasterKey', async function (done) {
+ Parse.Cloud.beforeFind(
+ 'beforeFind',
+ () => {
+ throw 'before find should have resolved using masterKey.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const obj = new Parse.Object('beforeFind');
+ obj.set('foo', 'bar');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+
+ const query = new Parse.Query('beforeFind');
+ const first = await query.first({ useMasterKey: true });
+ expect(first).toBeDefined();
+ expect(first.id).toBe(obj.id);
+ done();
+ });
+
+ it('basic beforeDelete skipWithMasterKey', async function (done) {
+ Parse.Cloud.beforeDelete(
+ 'beforeFind',
+ () => {
+ throw 'before find should have resolved using masterKey.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const obj = new Parse.Object('beforeFind');
+ obj.set('foo', 'bar');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ await obj.destroy({ useMasterKey: true });
+ done();
+ });
+
+ it('basic beforeSaveFile skipWithMasterKey', async done => {
+ Parse.Cloud.beforeSave(
+ Parse.File,
+ () => {
+ throw 'beforeSaveFile should have resolved using master key.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ done();
+ });
+
+ it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)('basic beforeSave Parse.Config skipWithMasterKey', async () => {
+ Parse.Cloud.beforeSave(
+ Parse.Config,
+ () => {
+ throw 'beforeSaveFile should have resolved using master key.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const config = await testConfig();
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ });
+
+ it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)('basic afterSave Parse.Config skipWithMasterKey', async () => {
+ Parse.Cloud.afterSave(
+ Parse.Config,
+ () => {
+ throw 'beforeSaveFile should have resolved using master key.';
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const config = await testConfig();
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ });
+
+ it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) {
+ Parse.Cloud.beforeSave(
+ 'BeforeSave',
+ () => {
+ throw 'beforeSaveFile should have resolved using master key.';
+ },
+ {
+ fields: ['foo'],
+ validateMasterKey: true,
+ skipWithMasterKey: true,
+ }
+ );
+
+ const obj = new Parse.Object('BeforeSave');
+ try {
+ await obj.save(null, { useMasterKey: true });
+ fail('function should have failed.');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please specify data for foo.');
+ done();
+ }
+ });
+
+ it('beforeSave validateMasterKey and skipWithMasterKey success', async function (done) {
+ Parse.Cloud.beforeSave(
+ 'BeforeSave',
+ () => {
+ throw 'beforeSaveFile should have resolved using master key.';
+ },
+ {
+ fields: ['foo'],
+ validateMasterKey: true,
+ skipWithMasterKey: true,
+ }
+ );
+
+ const obj = new Parse.Object('BeforeSave');
+ obj.set('foo', 'bar');
+ try {
+ await obj.save(null, { useMasterKey: true });
+ done();
+ } catch (error) {
+ fail('error should not have been called.');
+ }
+ });
+
+ it('basic beforeSave requireUserKey on User Class', async function (done) {
+ Parse.Cloud.beforeSave(Parse.User, () => {}, {
+ requireUser: true,
+ requireUserKeys: ['name'],
+ });
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'p@ssword');
+ user.set('name', 'foo');
+ expect(user.get('name')).toBe('foo');
+ done();
+ });
+
+ it('basic beforeSave requireUserKey rejection', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUser: true,
+ requireUserKeys: ['name'],
+ });
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ try {
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+ fail('should not have been able to save without userkey');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please set data for name on your account.');
+ done();
+ }
+ });
+
+ it('basic beforeSave requireUserKey without user', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUserKeys: ['name'],
+ });
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ try {
+ await obj.save();
+ fail('should not have been able to save without user');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Please login to make this request.');
+ done();
+ }
+ });
+
+ it('basic beforeSave requireUserKey as admin', async function (done) {
+ Parse.Cloud.beforeSave(Parse.User, () => {}, {
+ fields: {
+ admin: {
+ default: false,
+ constant: true,
+ },
+ },
+ });
+ Parse.Cloud.define(
+ 'secureFunction',
+ () => {
+ return "Here's all the secure data!";
+ },
+ {
+ requireUserKeys: {
+ admin: {
+ options: true,
+ error: 'Unauthorized.',
+ },
+ },
+ }
+ );
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'p@ssword');
+ user.set('admin', true);
+ await user.signUp();
+ expect(user.get('admin')).toBe(false);
+ try {
+ await Parse.Cloud.run('secureFunction');
+ fail('function should only be available to admin users');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Unauthorized.');
+ }
+ done();
+ });
+
+ it('basic beforeSave requireUserKey as custom function', async function (done) {
+ Parse.Cloud.beforeSave(Parse.User, () => {}, {
+ fields: {
+ accType: {
+ default: 'normal',
+ constant: true,
+ },
+ },
+ });
+ Parse.Cloud.define(
+ 'secureFunction',
+ () => {
+ return "Here's all the secure data!";
+ },
+ {
+ requireUserKeys: {
+ accType: {
+ options: val => {
+ return ['admin', 'admin2'].includes(val);
+ },
+ error: 'Unauthorized.',
+ },
+ },
+ }
+ );
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'p@ssword');
+ user.set('accType', 'admin');
+ await user.signUp();
+ expect(user.get('accType')).toBe('normal');
+ try {
+ await Parse.Cloud.run('secureFunction');
+ fail('function should only be available to admin users');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Unauthorized.');
+ }
+ done();
+ });
+
+ it('basic beforeSave allow requireUserKey as custom function', async function (done) {
+ Parse.Cloud.beforeSave(Parse.User, () => {}, {
+ fields: {
+ accType: {
+ default: 'admin',
+ constant: true,
+ },
+ },
+ });
+ Parse.Cloud.define(
+ 'secureFunction',
+ () => {
+ return "Here's all the secure data!";
+ },
+ {
+ requireUserKeys: {
+ accType: {
+ options: val => {
+ return ['admin', 'admin2'].includes(val);
+ },
+ error: 'Unauthorized.',
+ },
+ },
+ }
+ );
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'p@ssword');
+ await user.signUp();
+ expect(user.get('accType')).toBe('admin');
+ const result = await Parse.Cloud.run('secureFunction');
+ expect(result).toBe("Here's all the secure data!");
+ done();
+ });
+
+ it('basic beforeSave requireUser', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUser: true,
+ });
+
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please login to continue.');
+ done();
+ });
+ });
+
+ it('basic validator requireAnyUserRoles', async function (done) {
+ Parse.Cloud.define(
+ 'cloudFunction',
+ () => {
+ return true;
+ },
+ {
+ requireUser: true,
+ requireAnyUserRoles: ['Admin'],
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ try {
+ await Parse.Cloud.run('cloudFunction');
+ fail('cloud validator should have failed.');
+ } catch (e) {
+ expect(e.message).toBe('Validation failed. User does not match the required roles.');
+ }
+ const roleACL = new Parse.ACL();
+ roleACL.setPublicReadAccess(true);
+ const role = new Parse.Role('Admin', roleACL);
+ role.getUsers().add(user);
+ await role.save({ useMasterKey: true });
+ await Parse.Cloud.run('cloudFunction');
+ done();
+ });
+
+ it('basic validator requireAllUserRoles', async function (done) {
+ Parse.Cloud.define(
+ 'cloudFunction',
+ () => {
+ return true;
+ },
+ {
+ requireUser: true,
+ requireAllUserRoles: ['Admin', 'Admin2'],
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ try {
+ await Parse.Cloud.run('cloudFunction');
+ fail('cloud validator should have failed.');
+ } catch (e) {
+ expect(e.message).toBe('Validation failed. User does not match all the required roles.');
+ }
+ const roleACL = new Parse.ACL();
+ roleACL.setPublicReadAccess(true);
+ const role = new Parse.Role('Admin', roleACL);
+ role.getUsers().add(user);
+
+ const role2 = new Parse.Role('Admin2', roleACL);
+ role2.getUsers().add(user);
+ await role.save({ useMasterKey: true });
+ await role2.save({ useMasterKey: true });
+ await Parse.Cloud.run('cloudFunction');
+ done();
+ });
+
+ it('allow requireAnyUserRoles to be a function', async function (done) {
+ Parse.Cloud.define(
+ 'cloudFunction',
+ () => {
+ return true;
+ },
+ {
+ requireUser: true,
+ requireAnyUserRoles: () => {
+ return ['Admin Func'];
+ },
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ try {
+ await Parse.Cloud.run('cloudFunction');
+ fail('cloud validator should have failed.');
+ } catch (e) {
+ expect(e.message).toBe('Validation failed. User does not match the required roles.');
+ }
+ const roleACL = new Parse.ACL();
+ roleACL.setPublicReadAccess(true);
+ const role = new Parse.Role('Admin Func', roleACL);
+ role.getUsers().add(user);
+ await role.save({ useMasterKey: true });
+ await Parse.Cloud.run('cloudFunction');
+ done();
+ });
+
+ it('allow requireAllUserRoles to be a function', async function (done) {
+ Parse.Cloud.define(
+ 'cloudFunction',
+ () => {
+ return true;
+ },
+ {
+ requireUser: true,
+ requireAllUserRoles: () => {
+ return ['AdminA', 'AdminB'];
+ },
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ try {
+ await Parse.Cloud.run('cloudFunction');
+ fail('cloud validator should have failed.');
+ } catch (e) {
+ expect(e.message).toBe('Validation failed. User does not match all the required roles.');
+ }
+ const roleACL = new Parse.ACL();
+ roleACL.setPublicReadAccess(true);
+ const role = new Parse.Role('AdminA', roleACL);
+ role.getUsers().add(user);
+
+ const role2 = new Parse.Role('AdminB', roleACL);
+ role2.getUsers().add(user);
+ await role.save({ useMasterKey: true });
+ await role2.save({ useMasterKey: true });
+ await Parse.Cloud.run('cloudFunction');
+ done();
+ });
+
+ it('basic requireAllUserRoles but no user', async function (done) {
+ Parse.Cloud.define(
+ 'cloudFunction',
+ () => {
+ return true;
+ },
+ {
+ requireAllUserRoles: ['Admin'],
+ }
+ );
+ try {
+ await Parse.Cloud.run('cloudFunction');
+ fail('cloud validator should have failed.');
+ } catch (e) {
+ expect(e.message).toBe('Validation failed. Please login to continue.');
+ }
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ const roleACL = new Parse.ACL();
+ roleACL.setPublicReadAccess(true);
+ const role = new Parse.Role('Admin', roleACL);
+ role.getUsers().add(user);
+ await role.save({ useMasterKey: true });
+ await Parse.Cloud.run('cloudFunction');
+ done();
+ });
+
+ it('basic beforeSave requireMaster', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireMaster: true,
+ });
+
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual(
+ 'Validation failed. Master key is required to complete this request.'
+ );
+ done();
+ });
+ });
+
+ it('basic beforeSave master', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUser: true,
+ });
+
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ await obj.save(null, { useMasterKey: true });
+ done();
+ });
+
+ it('basic beforeSave validateMasterKey', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
+ requireUser: true,
+ validateMasterKey: true,
+ });
+
+ const obj = new Parse.Object('BeforeSaveFail');
+ obj.set('foo', 'bar');
+ obj
+ .save(null, { useMasterKey: true })
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please login to continue.');
+ done();
+ });
+ });
+
+ it('basic beforeSave requireKeys', function (done) {
+ Parse.Cloud.beforeSave('beforeSaveRequire', () => {}, {
+ fields: {
+ foo: {
+ required: true,
+ },
+ bar: {
+ required: true,
+ },
+ },
+ });
+ const obj = new Parse.Object('beforeSaveRequire');
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(() => {
+ fail('function should have failed.');
+ })
+ .catch(error => {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed. Please specify data for bar.');
+ done();
+ });
+ });
+
+ it('basic beforeSave constantKeys', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSave', () => {}, {
+ fields: {
+ foo: {
+ constant: true,
+ default: 'bar',
+ },
+ },
+ });
+ const obj = new Parse.Object('BeforeSave');
+ obj.set('foo', 'far');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ obj.set('foo', 'yolo');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ done();
+ });
+
+ it('basic beforeSave defaultKeys', async function (done) {
+ Parse.Cloud.beforeSave('BeforeSave', () => {}, {
+ fields: {
+ foo: {
+ default: 'bar',
+ },
+ },
+ });
+ const obj = new Parse.Object('BeforeSave');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ obj.set('foo', 'yolo');
+ await obj.save();
+ expect(obj.get('foo')).toBe('yolo');
+ done();
+ });
+
+ it('validate beforeSave', async done => {
+ Parse.Cloud.beforeSave('MyObject', () => {}, validatorSuccess);
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ try {
+ await myObject.save();
+ done();
+ } catch (e) {
+ fail('before save should not have failed.');
+ }
+ });
+
+ it('validate beforeSave fail', async done => {
+ Parse.Cloud.beforeSave('MyObject', () => {}, validatorFail);
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ try {
+ await myObject.save();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate afterSave', async done => {
+ Parse.Cloud.afterSave(
+ 'MyObject',
+ () => {
+ done();
+ },
+ validatorSuccess
+ );
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ try {
+ await myObject.save();
+ } catch (e) {
+ fail('before save should not have failed.');
+ }
+ });
+
+ it('validate afterSave fail', async done => {
+ Parse.Cloud.afterSave(
+ 'MyObject',
+ () => {
+ fail('this should not be called.');
+ },
+ validatorFail
+ );
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ setTimeout(() => {
+ done();
+ }, 1000);
+ });
+
+ it('validate beforeDelete', async done => {
+ Parse.Cloud.beforeDelete('MyObject', () => {}, validatorSuccess);
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ await myObject.destroy();
+ done();
+ } catch (e) {
+ fail('before delete should not have failed.');
+ }
+ });
+
+ it('validate beforeDelete fail', async done => {
+ Parse.Cloud.beforeDelete(
+ 'MyObject',
+ () => {
+ fail('this should not be called.');
+ },
+ validatorFail
+ );
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ await myObject.destroy();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate afterDelete', async done => {
+ Parse.Cloud.afterDelete(
+ 'MyObject',
+ () => {
+ done();
+ },
+ validatorSuccess
+ );
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ await myObject.destroy();
+ } catch (e) {
+ fail('after delete should not have failed.');
+ }
+ });
+
+ it('validate afterDelete fail', async done => {
+ Parse.Cloud.afterDelete(
+ 'MyObject',
+ () => {
+ fail('this should not be called.');
+ },
+ validatorFail
+ );
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ await myObject.destroy();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate beforeFind', async done => {
+ Parse.Cloud.beforeFind('MyObject', () => {}, validatorSuccess);
+ try {
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObjectQuery = new Parse.Query(MyObject);
+ await myObjectQuery.find();
+ done();
+ } catch (e) {
+ fail('beforeFind should not have failed.');
+ }
+ });
+ it('validate beforeFind fail', async done => {
+ Parse.Cloud.beforeFind('MyObject', () => {}, validatorFail);
+ try {
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObjectQuery = new Parse.Query(MyObject);
+ await myObjectQuery.find();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate afterFind', async done => {
+ Parse.Cloud.afterFind('MyObject', () => {}, validatorSuccess);
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ const myObjectQuery = new Parse.Query(MyObject);
+ await myObjectQuery.find();
+ done();
+ } catch (e) {
+ fail('beforeFind should not have failed.');
+ }
+ });
+
+ it('validate afterFind fail', async done => {
+ Parse.Cloud.afterFind('MyObject', () => {}, validatorFail);
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ try {
+ const myObjectQuery = new Parse.Query(MyObject);
+ await myObjectQuery.find();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate beforeSaveFile', async done => {
+ Parse.Cloud.beforeSave(Parse.File, () => {}, validatorSuccess);
+
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ done();
+ });
+
+ it('validate beforeSaveFile fail', async done => {
+ Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail);
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate afterSaveFile', async done => {
+ Parse.Cloud.afterSave(Parse.File, () => {}, validatorSuccess);
+
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ done();
+ });
+
+ it('validate afterSaveFile fail', async done => {
+ Parse.Cloud.afterSave(Parse.File, () => {}, validatorFail);
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate beforeDeleteFile', async done => {
+ Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorSuccess);
+
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save();
+ await file.destroy();
+ done();
+ });
+
+ it('validate beforeDeleteFile fail', async done => {
+ Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorFail);
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save();
+ await file.destroy();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('validate afterDeleteFile', async done => {
+ Parse.Cloud.afterDelete(Parse.File, () => {}, validatorSuccess);
+
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save();
+ await file.destroy();
+ done();
+ });
+
+ it('validate afterDeleteFile fail', async done => {
+ Parse.Cloud.afterDelete(Parse.File, () => {}, validatorFail);
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save();
+ await file.destroy();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)('validate beforeSave Parse.Config', async () => {
+ Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess);
+ const config = await testConfig();
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ });
+
+ it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)('validate beforeSave Parse.Config fail', async () => {
+ Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail);
+ try {
+ await testConfig();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ }
+ });
+
+ it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => {
+ Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess);
+ const config = await testConfig();
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ });
+
+ it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)('validate afterSave Parse.Config fail', async () => {
+ Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail);
+ try {
+ await testConfig();
+ fail('cloud function should have failed.');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ }
+ });
+
+ it('Should have validator', async done => {
+ Parse.Cloud.define(
+ 'myFunction',
+ () => {},
+ () => {
+ throw 'error';
+ }
+ );
+ try {
+ await Parse.Cloud.run('myFunction');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
+ done();
+ }
+ });
+
+ it('does not log on valid config', () => {
+ Parse.Cloud.define('myFunction', () => {}, {
+ requireUser: true,
+ requireMaster: true,
+ validateMasterKey: false,
+ skipWithMasterKey: true,
+ requireUserKeys: {
+ Acc: {
+ constant: true,
+ options: ['A', 'B'],
+ required: true,
+ default: 'f',
+ error: 'a',
+ type: String,
+ },
+ },
+ fields: {
+ Acc: {
+ constant: true,
+ options: ['A', 'B'],
+ required: true,
+ default: 'f',
+ error: 'a',
+ type: String,
+ },
+ },
+ });
+ });
+ it('Logs on invalid config', () => {
+ const fields = [
+ {
+ field: 'requiredUser',
+ value: true,
+ error: 'requiredUser is not a supported parameter for Cloud Function validations.',
+ },
+ {
+ field: 'requireUser',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array',
+ },
+ {
+ field: 'requireMaster',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array',
+ },
+ {
+ field: 'validateMasterKey',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array',
+ },
+ {
+ field: 'skipWithMasterKey',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array',
+ },
+ {
+ field: 'requireAllUserRoles',
+ value: true,
+ error:
+ 'Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean',
+ },
+ {
+ field: 'requireAnyUserRoles',
+ value: true,
+ error:
+ 'Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean',
+ },
+ {
+ field: 'fields',
+ value: true,
+ error:
+ 'Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean',
+ },
+ {
+ field: 'requireUserKeys',
+ value: true,
+ error:
+ 'Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean',
+ },
+ ];
+ for (const field of fields) {
+ try {
+ Parse.Cloud.define('myFunction', () => {}, {
+ [field.field]: field.value,
+ });
+ fail(`Expected error registering invalid Cloud Function validation ${field.field}.`);
+ } catch (e) {
+ expect(e).toBe(field.error);
+ }
+ }
+ });
+
+ it('Logs on multiple invalid configs', () => {
+ const fields = [
+ {
+ field: 'otherKey',
+ value: true,
+ error: 'otherKey is not a supported parameter for Cloud Function validations.',
+ },
+ {
+ field: 'constant',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key constant. Expected boolean, actual array',
+ },
+ {
+ field: 'required',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key required. Expected boolean, actual array',
+ },
+ {
+ field: 'error',
+ value: [],
+ error:
+ 'Invalid type for Cloud Function validation key error. Expected string, actual array',
+ },
+ ];
+ for (const field of fields) {
+ try {
+ Parse.Cloud.define('myFunction', () => {}, {
+ fields: {
+ name: {
+ [field.field]: field.value,
+ },
+ },
+ });
+ fail(`Expected error registering invalid Cloud Function validation ${field.field}.`);
+ } catch (e) {
+ expect(e).toBe(field.error);
+ }
+ try {
+ Parse.Cloud.define('myFunction', () => {}, {
+ requireUserKeys: {
+ name: {
+ [field.field]: field.value,
+ },
+ },
+ });
+ fail(`Expected error registering invalid Cloud Function validation ${field.field}.`);
+ } catch (e) {
+ expect(e).toBe(field.error);
+ }
+ }
+ });
+
+ it('set params options function async', async () => {
+ Parse.Cloud.define(
+ 'hello',
+ () => {
+ return 'Hello world!';
+ },
+ {
+ fields: {
+ data: {
+ type: String,
+ required: true,
+ options: async val => {
+ await new Promise(resolve => {
+ setTimeout(resolve, 500);
+ });
+ return val === 'f';
+ },
+ error: 'Validation failed.',
+ },
+ },
+ }
+ );
+ try {
+ await Parse.Cloud.run('hello', { data: 'd' });
+ fail('validation should have failed');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Validation failed.');
+ }
+ const result = await Parse.Cloud.run('hello', { data: 'f' });
+ expect(result).toBe('Hello world!');
+ });
+
+ it('basic beforeSave requireUserKey as custom async function', async () => {
+ Parse.Cloud.beforeSave(Parse.User, () => {}, {
+ fields: {
+ accType: {
+ default: 'normal',
+ constant: true,
+ },
+ },
+ });
+ Parse.Cloud.define(
+ 'secureFunction',
+ () => {
+ return "Here's all the secure data!";
+ },
+ {
+ requireUserKeys: {
+ accType: {
+ options: async val => {
+ await new Promise(resolve => {
+ setTimeout(resolve, 500);
+ });
+ return ['admin', 'admin2'].includes(val);
+ },
+ error: 'Unauthorized.',
+ },
+ },
+ }
+ );
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'p@ssword');
+ user.set('accType', 'admin');
+ await user.signUp();
+ expect(user.get('accType')).toBe('normal');
+ try {
+ await Parse.Cloud.run('secureFunction');
+ fail('function should only be available to admin users');
+ } catch (error) {
+ expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
+ expect(error.message).toEqual('Unauthorized.');
+ }
+ });
+});
diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js
index 3e5fdaa029..941d896aae 100644
--- a/spec/CloudCode.spec.js
+++ b/spec/CloudCode.spec.js
@@ -1,796 +1,5110 @@
-"use strict"
-const Parse = require("parse/node");
-const request = require('request');
-const rp = require('request-promise');
-const InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter;
+'use strict';
+const Config = require('../lib/Config');
+const Parse = require('parse/node');
+const ParseServer = require('../lib/index').ParseServer;
+const request = require('../lib/request');
+const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter')
+ .InMemoryCacheAdapter;
+const Utils = require('../lib/Utils');
+
+const mockAdapter = {
+ createFile: async filename => ({
+ name: filename,
+ location: `http://www.somewhere.com/${filename}`,
+ }),
+ deleteFile: () => { },
+ getFileData: () => { },
+ getFileLocation: (config, filename) => `http://www.somewhere.com/${filename}`,
+ validateFilename: () => {
+ return null;
+ },
+};
describe('Cloud Code', () => {
it('can load absolute cloud code file', done => {
- reconfigureServer({ cloud: __dirname + '/cloud/cloudCodeRelativeFile.js' })
- .then(() => {
- Parse.Cloud.run('cloudCodeInFile', {}, result => {
+ reconfigureServer({
+ cloud: __dirname + '/cloud/cloudCodeRelativeFile.js',
+ }).then(() => {
+ Parse.Cloud.run('cloudCodeInFile', {}).then(result => {
expect(result).toEqual('It is possible to define cloud code in a file.');
done();
});
- })
+ });
});
it('can load relative cloud code file', done => {
- reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' })
- .then(() => {
- Parse.Cloud.run('cloudCodeInFile', {}, result => {
+ reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then(() => {
+ Parse.Cloud.run('cloudCodeInFile', {}).then(result => {
expect(result).toEqual('It is possible to define cloud code in a file.');
done();
});
- })
+ });
+ });
+
+ it('can load cloud code as a module', async () => {
+ process.env.npm_package_type = 'module';
+ await reconfigureServer({ appId: 'test1', cloud: './spec/cloud/cloudCodeModuleFile.js' });
+ const result = await Parse.Cloud.run('cloudCodeInFile');
+ expect(result).toEqual('It is possible to define cloud code in a file.');
+ delete process.env.npm_package_type;
+ });
+
+ it('cloud code must be valid type', async () => {
+ spyOn(console, 'error').and.callFake(() => { });
+ await expectAsync(reconfigureServer({ cloud: true })).toBeRejectedWith(
+ "argument 'cloud' must either be a string or a function"
+ );
+ });
+
+ it('should wait for cloud code to load', async () => {
+ await reconfigureServer({ appId: 'test3' });
+ const initiated = new Date();
+ const parseServer = await new ParseServer({
+ ...defaultConfiguration,
+ appId: 'test3',
+ masterKey: 'test',
+ serverURL: 'http://localhost:12668/parse',
+ async cloud() {
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ Parse.Cloud.beforeSave('Test', () => {
+ throw 'Cannot save.';
+ });
+ },
+ }).start();
+ const express = require('express');
+ const app = express();
+ app.use('/parse', parseServer.app);
+ const server = app.listen(12668);
+ const now = new Date();
+ expect(now.getTime() - initiated.getTime() > 1000).toBeTrue();
+ await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith(
+ new Parse.Error(141, 'Cannot save.')
+ );
+ await new Promise(resolve => server.close(resolve));
});
it('can create functions', done => {
- Parse.Cloud.define('hello', (req, res) => {
- res.success('Hello world!');
+ Parse.Cloud.define('hello', () => {
+ return 'Hello world!';
});
- Parse.Cloud.run('hello', {}, result => {
+ Parse.Cloud.run('hello', {}).then(result => {
expect(result).toEqual('Hello world!');
done();
});
});
- it('is cleared cleared after the previous test', done => {
- Parse.Cloud.run('hello', {})
- .catch(error => {
- expect(error.code).toEqual(141);
- done();
- });
+ it('can get config', () => {
+ const config = Parse.Server;
+ let currentConfig = Config.get('test');
+ const server = require('../lib/cloud-code/Parse.Server');
+ expect(Object.keys(config)).toEqual(Object.keys({ ...currentConfig, ...server }));
+ config.silent = false;
+ Parse.Server = config;
+ currentConfig = Config.get('test');
+ expect(currentConfig.silent).toBeFalse();
});
- it('basic beforeSave rejection', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveFail', function(req, res) {
- res.error('You shall not pass!');
- });
-
- var obj = new Parse.Object('BeforeSaveFail');
- obj.set('foo', 'bar');
- obj.save().then(() => {
- fail('Should not have been able to save BeforeSaveFailure class.');
- done();
- }, () => {
- done();
- })
+ it('can get curent version', () => {
+ const version = require('../package.json').version;
+ const currentConfig = Config.get('test');
+ expect(Parse.Server.version).toBeDefined();
+ expect(currentConfig.version).toBeDefined();
+ expect(Parse.Server.version).toEqual(version);
});
- it('beforeSave rejection with custom error code', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function (req, res) {
- res.error(999, 'Nope');
+ it('show warning on duplicate cloud functions', done => {
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'warn').and.callFake(() => { });
+ Parse.Cloud.define('hello', () => {
+ return 'Hello world!';
});
-
- var obj = new Parse.Object('BeforeSaveFailWithErrorCode');
- obj.set('foo', 'bar');
- obj.save().then(function() {
- fail('Should not have been able to save BeforeSaveFailWithErrorCode class.');
- done();
- }, function(error) {
- expect(error.code).toEqual(999);
- expect(error.message).toEqual('Nope');
- done();
+ Parse.Cloud.define('hello', () => {
+ return 'Hello world!';
});
+ expect(logger.warn).toHaveBeenCalledWith(
+ 'Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored.'
+ );
+ done();
});
- it('basic beforeSave rejection via promise', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function (req, res) {
- var query = new Parse.Query('Yolo');
- query.find().then(() => {
- res.error('Nope');
- }, () => {
- res.success();
- });
- });
-
- var obj = new Parse.Object('BeforeSaveFailWithPromise');
- obj.set('foo', 'bar');
- obj.save().then(function() {
- fail('Should not have been able to save BeforeSaveFailure class.');
- done();
- }, function(error) {
+ it('is cleared cleared after the previous test', done => {
+ Parse.Cloud.run('hello', {}).catch(error => {
expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
- expect(error.message).toEqual('Nope');
done();
- })
+ });
});
- it('test beforeSave changed object success', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
- req.object.set('foo', 'baz');
- res.success();
+ it('basic beforeSave rejection', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', function () {
+ throw new Error('You shall not pass!');
});
- var obj = new Parse.Object('BeforeSaveChanged');
+ const obj = new Parse.Object('BeforeSaveFail');
obj.set('foo', 'bar');
- obj.save().then(function() {
- var query = new Parse.Query('BeforeSaveChanged');
- query.get(obj.id).then(function(objAgain) {
- expect(objAgain.get('foo')).toEqual('baz');
+ obj.save().then(
+ () => {
+ fail('Should not have been able to save BeforeSaveFailure class.');
done();
- }, function(error) {
- fail(error);
+ },
+ () => {
done();
- });
- }, function(error) {
- fail(error);
- done();
- });
+ }
+ );
});
- it('test beforeSave returns value on create and update', (done) => {
- Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
- req.object.set('foo', 'baz');
- res.success();
+ it('returns an error', done => {
+ Parse.Cloud.define('cloudCodeWithError', () => {
+ /* eslint-disable no-undef */
+ foo.bar();
+ /* eslint-enable no-undef */
+ return 'I better throw an error.';
});
- var obj = new Parse.Object('BeforeSaveChanged');
- obj.set('foo', 'bing');
- obj.save().then(() =>Â {
- expect(obj.get('foo')).toEqual('baz');
- obj.set('foo', 'bar');
- return obj.save().then(() =>Â {
- expect(obj.get('foo')).toEqual('baz');
+ Parse.Cloud.run('cloudCodeWithError').then(
+ () => done.fail('should not succeed'),
+ e => {
+ expect(e).toEqual(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'foo is not defined'));
done();
- })
- })
+ }
+ );
});
- it('test afterSave ran and created an object', function(done) {
- Parse.Cloud.afterSave('AfterSaveTest', function(req) {
- var obj = new Parse.Object('AfterSaveProof');
- obj.set('proof', req.object.id);
- obj.save();
+ it('returns an empty error', done => {
+ Parse.Cloud.define('cloudCodeWithError', () => {
+ throw null;
});
- var obj = new Parse.Object('AfterSaveTest');
- obj.save();
-
- setTimeout(function() {
- var query = new Parse.Query('AfterSaveProof');
- query.equalTo('proof', obj.id);
- query.find().then(function(results) {
- expect(results.length).toEqual(1);
+ Parse.Cloud.run('cloudCodeWithError').then(
+ () => done.fail('should not succeed'),
+ e => {
+ expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toEqual('Script failed.');
done();
- }, function(error) {
- fail(error);
- done();
- });
- }, 500);
+ }
+ );
});
- it('test beforeSave happens on update', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
- req.object.set('foo', 'baz');
- res.success();
+ it('beforeFind can throw string', async function (done) {
+ Parse.Cloud.beforeFind('beforeFind', () => {
+ throw 'throw beforeFind';
});
-
- var obj = new Parse.Object('BeforeSaveChanged');
+ const obj = new Parse.Object('beforeFind');
obj.set('foo', 'bar');
- obj.save().then(function() {
- obj.set('foo', 'bar');
- return obj.save();
- }).then(function() {
- var query = new Parse.Query('BeforeSaveChanged');
- return query.get(obj.id).then(function(objAgain) {
- expect(objAgain.get('foo')).toEqual('baz');
- done();
- });
- }, function(error) {
- fail(error);
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ try {
+ const query = new Parse.Query('beforeFind');
+ await query.first();
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBe('throw beforeFind');
done();
- });
+ }
});
- it('test beforeDelete failure', function(done) {
- Parse.Cloud.beforeDelete('BeforeDeleteFail', function(req, res) {
- res.error('Nope');
+ describe('beforeFind without DB operations', () => {
+ let findSpy;
+
+ beforeEach(() => {
+ const config = Config.get('test');
+ const databaseAdapter = config.database.adapter;
+ findSpy = spyOn(databaseAdapter, 'find').and.callThrough();
});
- var obj = new Parse.Object('BeforeDeleteFail');
- var id;
- obj.set('foo', 'bar');
- obj.save().then(() => {
- id = obj.id;
- return obj.destroy();
- }).then(() => {
- fail('obj.destroy() should have failed, but it succeeded');
- done();
- }, (error) => {
- expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
- expect(error.message).toEqual('Nope');
+ it('beforeFind can return object without DB operation', async () => {
+ Parse.Cloud.beforeFind('TestObject', () => {
+ return new Parse.Object('TestObject', { foo: 'bar' });
+ });
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.objects).toBeDefined();
+ expect(req.objects[0].get('foo')).toBe('bar');
+ });
- var objAgain = new Parse.Object('BeforeDeleteFail', {objectId: id});
- return objAgain.fetch();
- }).then((objAgain) => {
- if (objAgain) {
- expect(objAgain.get('foo')).toEqual('bar');
- } else {
- fail("unable to fetch the object ", id);
- }
- done();
- }, (error) => {
- // We should have been able to fetch the object again
- fail(error);
+ const newObj = await new Parse.Query('TestObject').first();
+ expect(newObj.className).toBe('TestObject');
+ expect(newObj.toJSON()).toEqual({ foo: 'bar' });
+ expect(findSpy).not.toHaveBeenCalled();
+ await newObj.save();
});
- });
- it('basic beforeDelete rejection via promise', function(done) {
- Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function (req, res) {
- var query = new Parse.Query('Yolo');
- query.find().then(() => {
- res.error('Nope');
- }, () => {
- res.success();
+ it('beforeFind can return array of objects without DB operation', async () => {
+ Parse.Cloud.beforeFind('TestObject', () => {
+ return [new Parse.Object('TestObject', { foo: 'bar' })];
+ });
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.objects).toBeDefined();
+ expect(req.objects[0].get('foo')).toBe('bar');
});
- });
- var obj = new Parse.Object('BeforeDeleteFailWithPromise');
- obj.set('foo', 'bar');
- obj.save().then(function() {
- fail('Should not have been able to save BeforeSaveFailure class.');
- done();
- }, function(error) {
- expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
- expect(error.message).toEqual('Nope');
+ const newObj = await new Parse.Query('TestObject').first();
+ expect(newObj.className).toBe('TestObject');
+ expect(newObj.toJSON()).toEqual({ foo: 'bar' });
+ expect(findSpy).not.toHaveBeenCalled();
+ await newObj.save();
+ });
- done();
- })
- });
+ it('beforeFind can return object for get query without DB operation', async () => {
+ Parse.Cloud.beforeFind('TestObject', () => {
+ return [new Parse.Object('TestObject', { foo: 'bar' })];
+ });
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.objects).toBeDefined();
+ expect(req.objects[0].get('foo')).toBe('bar');
+ });
- it('test afterDelete ran and created an object', function(done) {
- Parse.Cloud.afterDelete('AfterDeleteTest', function(req) {
- var obj = new Parse.Object('AfterDeleteProof');
- obj.set('proof', req.object.id);
- obj.save();
- });
+ const testObj = new Parse.Object('TestObject');
+ await testObj.save();
+ findSpy.calls.reset();
- var obj = new Parse.Object('AfterDeleteTest');
- obj.save().then(function() {
- obj.destroy();
+ const newObj = await new Parse.Query('TestObject').get(testObj.id);
+ expect(newObj.className).toBe('TestObject');
+ expect(newObj.toJSON()).toEqual({ foo: 'bar' });
+ expect(findSpy).not.toHaveBeenCalled();
+ await newObj.save();
});
- setTimeout(function() {
- var query = new Parse.Query('AfterDeleteProof');
- query.equalTo('proof', obj.id);
- query.find().then(function(results) {
- expect(results.length).toEqual(1);
- done();
- }, function(error) {
- fail(error);
- done();
+ it('beforeFind can return empty array without DB operation', async () => {
+ Parse.Cloud.beforeFind('TestObject', () => {
+ return [];
+ });
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.objects.length).toBe(0);
});
- }, 500);
+
+ const obj = new Parse.Object('TestObject');
+ await obj.save();
+ findSpy.calls.reset();
+
+ const newObj = await new Parse.Query('TestObject').first();
+ expect(newObj).toBeUndefined();
+ expect(findSpy).not.toHaveBeenCalled();
+ });
});
- it('test cloud function return types', function(done) {
- Parse.Cloud.define('foo', function(req, res) {
- res.success({
- object: {
- __type: 'Object',
- className: 'Foo',
- objectId: '123',
- x: 2,
- relation: {
- __type: 'Object',
- className: 'Bar',
- objectId: '234',
- x: 3
- }
- },
- array: [{
- __type: 'Object',
- className: 'Bar',
- objectId: '345',
- x: 2
- }],
- a: 2
+ describe('beforeFind security with returned objects', () => {
+ let userA;
+ let userB;
+ let secret;
+
+ beforeEach(async () => {
+ userA = new Parse.User();
+ userA.setUsername('userA_' + Date.now());
+ userA.setPassword('passA');
+ await userA.signUp();
+
+ userB = new Parse.User();
+ userB.setUsername('userB_' + Date.now());
+ userB.setPassword('passB');
+ await userB.signUp();
+
+ // Create an object readable only by userB
+ const acl = new Parse.ACL();
+ acl.setPublicReadAccess(false);
+ acl.setPublicWriteAccess(false);
+ acl.setReadAccess(userB.id, true);
+ acl.setWriteAccess(userB.id, true);
+
+ secret = new Parse.Object('SecretDoc');
+ secret.set('title', 'top');
+ secret.set('content', 'classified');
+ secret.setACL(acl);
+ await secret.save(null, { sessionToken: userB.getSessionToken() });
+
+ Parse.Cloud.beforeFind('SecretDoc', () => {
+ return [secret];
});
});
- Parse.Cloud.run('foo').then((result) => {
- expect(result.object instanceof Parse.Object).toBeTruthy();
- if (!result.object) {
- fail("Unable to run foo");
- done();
- return;
- }
- expect(result.object.className).toEqual('Foo');
- expect(result.object.get('x')).toEqual(2);
- var bar = result.object.get('relation');
- expect(bar instanceof Parse.Object).toBeTruthy();
- expect(bar.className).toEqual('Bar');
- expect(bar.get('x')).toEqual(3);
- expect(Array.isArray(result.array)).toEqual(true);
- expect(result.array[0] instanceof Parse.Object).toBeTruthy();
- expect(result.array[0].get('x')).toEqual(2);
- done();
+ it('should not expose objects not readable by current user', async () => {
+ const q = new Parse.Query('SecretDoc');
+ const results = await q.find({ sessionToken: userA.getSessionToken() });
+ expect(results.length).toBe(0);
});
- });
- it('test cloud function request params types', function(done) {
- Parse.Cloud.define('params', function(req, res) {
- expect(req.params.date instanceof Date).toBe(true);
- expect(req.params.date.getTime()).toBe(1463907600000);
- expect(req.params.dateList[0] instanceof Date).toBe(true);
- expect(req.params.dateList[0].getTime()).toBe(1463907600000);
- expect(req.params.complexStructure.date[0] instanceof Date).toBe(true);
- expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
- expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true);
- expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
- expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true);
- expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
- // Regression for #2294
- expect(req.params.file instanceof Parse.File).toBe(true);
- expect(req.params.file.url()).toEqual('https://some.url');
- // Regression for #2204
- expect(req.params.array).toEqual(['a', 'b', 'c']);
- expect(Array.isArray(req.params.array)).toBe(true);
- expect(req.params.arrayOfArray).toEqual([['a', 'b', 'c'], ['d', 'e','f']]);
- expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
- expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
- expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);
- return res.success({});
+ it('should allow authorized user to see their objects', async () => {
+ const q = new Parse.Query('SecretDoc');
+ const results = await q.find({ sessionToken: userB.getSessionToken() });
+ expect(results.length).toBe(1);
+ expect(results[0].id).toBe(secret.id);
+ expect(results[0].get('title')).toBe('top');
+ expect(results[0].get('content')).toBe('classified');
});
- let params = {
- 'date': {
- '__type': 'Date',
- 'iso': '2016-05-22T09:00:00.000Z'
- },
- 'dateList': [
- {
- '__type': 'Date',
- 'iso': '2016-05-22T09:00:00.000Z'
- }
- ],
- 'lol': 'hello',
- 'complexStructure': {
- 'date': [
- {
- '__type': 'Date',
- 'iso': '2016-05-22T09:00:00.000Z'
- }
- ],
- 'deepDate': {
- 'date': [
- {
- '__type': 'Date',
- 'iso': '2016-05-22T09:00:00.000Z'
- }
- ]
- },
- 'deepDate2': [
- {
- 'date': {
- '__type': 'Date',
- 'iso': '2016-05-22T09:00:00.000Z'
- }
- }
- ]
- },
- 'file': Parse.File.fromJSON({
- __type: 'File',
- name: 'name',
- url: 'https://some.url'
- }),
- 'array': ['a', 'b', 'c'],
- 'arrayOfArray': [['a', 'b', 'c'], ['d', 'e', 'f']]
- };
- Parse.Cloud.run('params', params).then((result) => {
- done();
+ it('should return OBJECT_NOT_FOUND on get() for unauthorized user', async () => {
+ const q = new Parse.Query('SecretDoc');
+ await expectAsync(
+ q.get(secret.id, { sessionToken: userA.getSessionToken() })
+ ).toBeRejectedWith(jasmine.objectContaining({ code: Parse.Error.OBJECT_NOT_FOUND }));
});
- });
- it('test cloud function should echo keys', function(done) {
- Parse.Cloud.define('echoKeys', function(req, res){
- return res.success({
- applicationId: Parse.applicationId,
- masterKey: Parse.masterKey,
- javascriptKey: Parse.javascriptKey
- })
+ it('should allow master key to bypass ACL filtering when returning objects', async () => {
+ const q = new Parse.Query('SecretDoc');
+ const results = await q.find({ useMasterKey: true });
+ expect(results.length).toBe(1);
+ expect(results[0].id).toBe(secret.id);
});
- Parse.Cloud.run('echoKeys').then((result) => {
- expect(result.applicationId).toEqual(Parse.applicationId);
- expect(result.masterKey).toEqual(Parse.masterKey);
- expect(result.javascriptKey).toEqual(Parse.javascriptKey);
- done();
+ it('should apply protectedFields masking after re-filtering', async () => {
+ // Configure protectedFields for SecretMask: mask `secretField` for everyone
+ const protectedFields = { SecretMask: { '*': ['secretField'] } };
+ await reconfigureServer({ protectedFields });
+
+ const user = new Parse.User();
+ user.setUsername('pfUser');
+ user.setPassword('pfPass');
+ await user.signUp();
+
+ // Object is publicly readable but has a protected field
+ const doc = new Parse.Object('SecretMask');
+ doc.set('name', 'visible');
+ doc.set('secretField', 'hiddenValue');
+ await doc.save(null, { useMasterKey: true });
+
+ Parse.Cloud.beforeFind('SecretMask', () => {
+ return [doc];
+ });
+
+ // Query as normal user; after re-filtering, secretField should be removed
+ const res = await new Parse.Query('SecretMask').first({ sessionToken: user.getSessionToken() });
+ expect(res).toBeDefined();
+ expect(res.get('name')).toBe('visible');
+ expect(res.get('secretField')).toBeUndefined();
+ const json = res.toJSON();
+ expect(Object.prototype.hasOwnProperty.call(json, 'secretField')).toBeFalse();
});
});
+ const { maybeRunAfterFindTrigger } = require('../lib/triggers');
- it('should properly create an object in before save', done => {
- Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
- req.object.set('foo', 'baz');
- res.success();
- });
+ describe('maybeRunAfterFindTrigger - direct function tests', () => {
+ const testConfig = {
+ applicationId: 'test',
+ logLevels: { triggerBeforeSuccess: 'info', triggerAfter: 'info' },
+ };
- Parse.Cloud.define('createBeforeSaveChangedObject', function(req, res){
- var obj = new Parse.Object('BeforeSaveChanged');
- obj.save().then(() =>Â {
- res.success(obj);
- })
- })
+ it('should convert Parse.Object instances to JSON when no trigger defined', async () => {
+ const className = 'TestParseObjectDirect_' + Date.now();
- Parse.Cloud.run('createBeforeSaveChangedObject').then((res) => {
- expect(res.get('foo')).toEqual('baz');
- done();
+ const parseObj1 = new Parse.Object(className);
+ parseObj1.set('name', 'test1');
+ parseObj1.id = 'obj1';
+
+ const parseObj2 = new Parse.Object(className);
+ parseObj2.set('name', 'test2');
+ parseObj2.id = 'obj2';
+
+ const result = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [parseObj1, parseObj2],
+ testConfig,
+ null,
+ {}
+ );
+
+ expect(result).toBeDefined();
+ expect(Array.isArray(result)).toBe(true);
+ expect(result.length).toBe(2);
+ expect(result[0].name).toBe('test1');
+ expect(result[1].name).toBe('test2');
});
- });
- it('dirtyKeys are set on update', done => {
- let triggerTime = 0;
- // Register a mock beforeSave hook
- Parse.Cloud.beforeSave('GameScore', (req, res) => {
- var object = req.object;
- expect(object instanceof Parse.Object).toBeTruthy();
- expect(object.get('fooAgain')).toEqual('barAgain');
- if (triggerTime == 0) {
- // Create
- expect(object.get('foo')).toEqual('bar');
- } else if (triggerTime == 1) {
- // Update
- expect(object.dirtyKeys()).toEqual(['foo']);
- expect(object.dirty('foo')).toBeTruthy();
- expect(object.get('foo')).toEqual('baz');
- } else {
- res.error();
- }
- triggerTime++;
- res.success();
+ it('should handle null/undefined objectsInput when no trigger', async () => {
+ const className = 'TestNullDirect_' + Date.now();
+
+ const resultNull = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ null,
+ testConfig,
+ null,
+ {}
+ );
+ expect(resultNull).toEqual([]);
+
+ const resultUndefined = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ undefined,
+ testConfig,
+ null,
+ {}
+ );
+ expect(resultUndefined).toEqual([]);
+
+ const resultEmpty = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [],
+ testConfig,
+ null,
+ {}
+ );
+ expect(resultEmpty).toEqual([]);
});
- let obj = new Parse.Object('GameScore');
- obj.set('foo', 'bar');
- obj.set('fooAgain', 'barAgain');
- obj.save().then(() => {
- // We only update foo
- obj.set('foo', 'baz');
- return obj.save();
- }).then(() => {
- // Make sure the checking has been triggered
- expect(triggerTime).toBe(2);
- done();
- }, function(error) {
- fail(error);
- done();
+ it('should handle plain object query with where clause', async () => {
+ const className = 'TestQueryWhereDirect_' + Date.now();
+ let receivedQuery = null;
+
+ Parse.Cloud.afterFind(className, req => {
+ receivedQuery = req.query;
+ return req.objects;
+ });
+
+ const mockObject = { id: 'test123', className: className, name: 'test' };
+
+ const result = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [mockObject],
+ testConfig,
+ { where: { name: 'test' }, limit: 10 },
+ {}
+ );
+
+ expect(receivedQuery).toBeInstanceOf(Parse.Query);
+ expect(result).toBeDefined();
});
- });
- it('test beforeSave unchanged success', function(done) {
- Parse.Cloud.beforeSave('BeforeSaveUnchanged', function(req, res) {
- res.success();
+ it('should handle plain object query without where clause', async () => {
+ const className = 'TestQueryNoWhereDirect_' + Date.now();
+ let receivedQuery = null;
+
+ Parse.Cloud.afterFind(className, req => {
+ receivedQuery = req.query;
+ return req.objects;
+ });
+
+ const mockObject = { id: 'test456', className: className, name: 'test' };
+ const pq = new Parse.Query(className).withJSON({ limit: 5, skip: 1 });
+
+ const result = await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [mockObject],
+ testConfig,
+ pq,
+ {}
+ );
+
+ expect(receivedQuery).toBeInstanceOf(Parse.Query);
+ const qJSON = receivedQuery.toJSON();
+ expect(qJSON.limit).toBe(5);
+ expect(qJSON.skip).toBe(1);
+ expect(qJSON.where).toEqual({});
+ expect(result).toBeDefined();
});
- var obj = new Parse.Object('BeforeSaveUnchanged');
- obj.set('foo', 'bar');
- obj.save().then(function() {
- done();
- }, function(error) {
- fail(error);
- done();
+ it('should create default query for invalid query parameter', async () => {
+ const className = 'TestInvalidQueryDirect_' + Date.now();
+ let receivedQuery = null;
+
+ Parse.Cloud.afterFind(className, req => {
+ receivedQuery = req.query;
+ return req.objects;
+ });
+
+ const mockObject = { id: 'test789', className: className, name: 'test' };
+
+ await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [mockObject],
+ testConfig,
+ 'invalid_query_string',
+ {}
+ );
+
+ expect(receivedQuery).toBeInstanceOf(Parse.Query);
+ expect(receivedQuery.className).toBe(className);
+
+ receivedQuery = null;
+
+ await maybeRunAfterFindTrigger(
+ 'afterFind',
+ null,
+ className,
+ [mockObject],
+ testConfig,
+ null,
+ {}
+ );
+
+ expect(receivedQuery).toBeInstanceOf(Parse.Query);
+ expect(receivedQuery.className).toBe(className);
});
});
- it('test beforeDelete success', function(done) {
- Parse.Cloud.beforeDelete('BeforeDeleteTest', function(req, res) {
- res.success();
+ it('beforeSave rejection with custom error code', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
+ throw new Parse.Error(999, 'Nope');
});
- var obj = new Parse.Object('BeforeDeleteTest');
+ const obj = new Parse.Object('BeforeSaveFailWithErrorCode');
obj.set('foo', 'bar');
- obj.save().then(function() {
- return obj.destroy();
- }).then(function() {
- var objAgain = new Parse.Object('BeforeDeleteTest', obj.id);
- return objAgain.fetch().then(fail, done);
- }, function(error) {
- fail(error);
- done();
- });
+ obj.save().then(
+ function () {
+ fail('Should not have been able to save BeforeSaveFailWithErrorCode class.');
+ done();
+ },
+ function (error) {
+ expect(error.code).toEqual(999);
+ expect(error.message).toEqual('Nope');
+ done();
+ }
+ );
});
- it('test save triggers get user', function(done) {
- Parse.Cloud.beforeSave('SaveTriggerUser', function(req, res) {
- if (req.user && req.user.id) {
- res.success();
- } else {
- res.error('No user present on request object for beforeSave.');
- }
+ it('basic beforeSave rejection via promise', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function () {
+ const query = new Parse.Query('Yolo');
+ return query.find().then(
+ () => {
+ throw 'Nope';
+ },
+ () => {
+ return Promise.response();
+ }
+ );
});
- Parse.Cloud.afterSave('SaveTriggerUser', function(req) {
- if (!req.user || !req.user.id) {
- console.log('No user present on request object for afterSave.');
+ const obj = new Parse.Object('BeforeSaveFailWithPromise');
+ obj.set('foo', 'bar');
+ obj.save().then(
+ function () {
+ fail('Should not have been able to save BeforeSaveFailure class.');
+ done();
+ },
+ function (error) {
+ expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
+ expect(error.message).toEqual('Nope');
+ done();
}
- });
+ );
+ });
- var user = new Parse.User();
- user.set("password", "asdf");
- user.set("email", "asdf@example.com");
- user.set("username", "zxcv");
- user.signUp(null, {
- success: function() {
- var obj = new Parse.Object('SaveTriggerUser');
- obj.save().then(function() {
+ it('test beforeSave changed object success', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('foo', 'baz');
+ });
+
+ const obj = new Parse.Object('BeforeSaveChanged');
+ obj.set('foo', 'bar');
+ obj.save().then(
+ function () {
+ const query = new Parse.Query('BeforeSaveChanged');
+ query.get(obj.id).then(
+ function (objAgain) {
+ expect(objAgain.get('foo')).toEqual('baz');
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test beforeSave with invalid field', async () => {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('length', 0);
+ });
+
+ const obj = new Parse.Object('BeforeSaveChanged');
+ obj.set('foo', 'bar');
+ try {
+ await obj.save();
+ fail('should not succeed');
+ } catch (e) {
+ expect(e.message).toBe('Invalid field name: length.');
+ }
+ });
+
+ it("test beforeSave changed object fail doesn't change object", async function () {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ if (req.object.has('fail')) {
+ return Promise.reject(new Error('something went wrong'));
+ }
+
+ return Promise.resolve();
+ });
+
+ const obj = new Parse.Object('BeforeSaveChanged');
+ obj.set('foo', 'bar');
+ await obj.save();
+ obj.set('foo', 'baz').set('fail', true);
+ try {
+ await obj.save();
+ } catch (e) {
+ await obj.fetch();
+ expect(obj.get('foo')).toBe('bar');
+ }
+ });
+
+ it('test beforeSave returns value on create and update', done => {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('foo', 'baz');
+ });
+
+ const obj = new Parse.Object('BeforeSaveChanged');
+ obj.set('foo', 'bing');
+ obj.save().then(() => {
+ expect(obj.get('foo')).toEqual('baz');
+ obj.set('foo', 'bar');
+ return obj.save().then(() => {
+ expect(obj.get('foo')).toEqual('baz');
+ done();
+ });
+ });
+ });
+
+ it('test beforeSave applies changes when beforeSave returns true', done => {
+ Parse.Cloud.beforeSave('Insurance', function (req) {
+ req.object.set('rate', '$49.99/Month');
+ return true;
+ });
+
+ const insurance = new Parse.Object('Insurance');
+ insurance.set('rate', '$5.00/Month');
+ insurance.save().then(insurance => {
+ expect(insurance.get('rate')).toEqual('$49.99/Month');
+ done();
+ });
+ });
+
+ it('test beforeSave applies changes and resolves returned promise', done => {
+ Parse.Cloud.beforeSave('Insurance', function (req) {
+ req.object.set('rate', '$49.99/Month');
+ return new Parse.Query('Pet').get(req.object.get('pet').id).then(pet => {
+ pet.set('healthy', true);
+ return pet.save();
+ });
+ });
+
+ const pet = new Parse.Object('Pet');
+ pet.set('healthy', false);
+ pet.save().then(pet => {
+ const insurance = new Parse.Object('Insurance');
+ insurance.set('pet', pet);
+ insurance.set('rate', '$5.00/Month');
+ insurance.save().then(insurance => {
+ expect(insurance.get('rate')).toEqual('$49.99/Month');
+ new Parse.Query('Pet').get(insurance.get('pet').id).then(pet => {
+ expect(pet.get('healthy')).toEqual(true);
+ done();
+ });
+ });
+ });
+ });
+
+ it('beforeSave should be called only if user fulfills permissions', async () => {
+ const triggeruser = new Parse.User();
+ triggeruser.setUsername('triggeruser');
+ triggeruser.setPassword('triggeruser');
+ await triggeruser.signUp();
+
+ const triggeruser2 = new Parse.User();
+ triggeruser2.setUsername('triggeruser2');
+ triggeruser2.setPassword('triggeruser2');
+ await triggeruser2.signUp();
+
+ const triggeruser3 = new Parse.User();
+ triggeruser3.setUsername('triggeruser3');
+ triggeruser3.setPassword('triggeruser3');
+ await triggeruser3.signUp();
+
+ const triggeruser4 = new Parse.User();
+ triggeruser4.setUsername('triggeruser4');
+ triggeruser4.setPassword('triggeruser4');
+ await triggeruser4.signUp();
+
+ const triggeruser5 = new Parse.User();
+ triggeruser5.setUsername('triggeruser5');
+ triggeruser5.setPassword('triggeruser5');
+ await triggeruser5.signUp();
+
+ const triggerroleacl = new Parse.ACL();
+ triggerroleacl.setPublicReadAccess(true);
+
+ const triggerrole = new Parse.Role();
+ triggerrole.setName('triggerrole');
+ triggerrole.setACL(triggerroleacl);
+ triggerrole.getUsers().add(triggeruser);
+ triggerrole.getUsers().add(triggeruser3);
+ await triggerrole.save();
+
+ const config = Config.get('test');
+ const schema = await config.database.loadSchema();
+ await schema.addClassIfNotExists(
+ 'triggerclass',
+ {
+ someField: { type: 'String' },
+ pointerToUser: { type: 'Pointer', targetClass: '_User' },
+ },
+ {
+ find: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ create: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ get: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ update: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ addField: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ delete: {
+ 'role:triggerrole': true,
+ [triggeruser.id]: true,
+ [triggeruser2.id]: true,
+ },
+ readUserFields: ['pointerToUser'],
+ writeUserFields: ['pointerToUser'],
+ },
+ {}
+ );
+
+ let called = 0;
+ Parse.Cloud.beforeSave('triggerclass', () => {
+ called++;
+ });
+
+ const triggerobject = new Parse.Object('triggerclass');
+ triggerobject.set('someField', 'someValue');
+ triggerobject.set('someField2', 'someValue');
+ const triggerobjectacl = new Parse.ACL();
+ triggerobjectacl.setPublicReadAccess(false);
+ triggerobjectacl.setPublicWriteAccess(false);
+ triggerobjectacl.setRoleReadAccess(triggerrole, true);
+ triggerobjectacl.setRoleWriteAccess(triggerrole, true);
+ triggerobjectacl.setReadAccess(triggeruser.id, true);
+ triggerobjectacl.setWriteAccess(triggeruser.id, true);
+ triggerobjectacl.setReadAccess(triggeruser2.id, true);
+ triggerobjectacl.setWriteAccess(triggeruser2.id, true);
+ triggerobject.setACL(triggerobjectacl);
+
+ await triggerobject.save(undefined, {
+ sessionToken: triggeruser.getSessionToken(),
+ });
+ expect(called).toBe(1);
+ await triggerobject.save(undefined, {
+ sessionToken: triggeruser.getSessionToken(),
+ });
+ expect(called).toBe(2);
+ await triggerobject.save(undefined, {
+ sessionToken: triggeruser2.getSessionToken(),
+ });
+ expect(called).toBe(3);
+ await triggerobject.save(undefined, {
+ sessionToken: triggeruser3.getSessionToken(),
+ });
+ expect(called).toBe(4);
+
+ const triggerobject2 = new Parse.Object('triggerclass');
+ triggerobject2.set('someField', 'someValue');
+ triggerobject2.set('someField22', 'someValue');
+ const triggerobjectacl2 = new Parse.ACL();
+ triggerobjectacl2.setPublicReadAccess(false);
+ triggerobjectacl2.setPublicWriteAccess(false);
+ triggerobjectacl2.setReadAccess(triggeruser.id, true);
+ triggerobjectacl2.setWriteAccess(triggeruser.id, true);
+ triggerobjectacl2.setReadAccess(triggeruser2.id, true);
+ triggerobjectacl2.setWriteAccess(triggeruser2.id, true);
+ triggerobjectacl2.setReadAccess(triggeruser5.id, true);
+ triggerobjectacl2.setWriteAccess(triggeruser5.id, true);
+ triggerobject2.setACL(triggerobjectacl2);
+
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser2.getSessionToken(),
+ });
+ expect(called).toBe(5);
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser2.getSessionToken(),
+ });
+ expect(called).toBe(6);
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser.getSessionToken(),
+ });
+ expect(called).toBe(7);
+
+ let catched = false;
+ try {
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser3.getSessionToken(),
+ });
+ } catch (e) {
+ catched = true;
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ expect(catched).toBe(true);
+ expect(called).toBe(7);
+
+ catched = false;
+ try {
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser4.getSessionToken(),
+ });
+ } catch (e) {
+ catched = true;
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ expect(catched).toBe(true);
+ expect(called).toBe(7);
+
+ catched = false;
+ try {
+ await triggerobject2.save(undefined, {
+ sessionToken: triggeruser5.getSessionToken(),
+ });
+ } catch (e) {
+ catched = true;
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ expect(catched).toBe(true);
+ expect(called).toBe(7);
+
+ const triggerobject3 = new Parse.Object('triggerclass');
+ triggerobject3.set('someField', 'someValue');
+ triggerobject3.set('someField33', 'someValue');
+
+ catched = false;
+ try {
+ await triggerobject3.save(undefined, {
+ sessionToken: triggeruser4.getSessionToken(),
+ });
+ } catch (e) {
+ catched = true;
+ expect(e.code).toBe(119);
+ }
+ expect(catched).toBe(true);
+ expect(called).toBe(7);
+
+ catched = false;
+ try {
+ await triggerobject3.save(undefined, {
+ sessionToken: triggeruser5.getSessionToken(),
+ });
+ } catch (e) {
+ catched = true;
+ expect(e.code).toBe(119);
+ }
+ expect(catched).toBe(true);
+ expect(called).toBe(7);
+ });
+
+ it('test afterSave ran and created an object', function (done) {
+ Parse.Cloud.afterSave('AfterSaveTest', function (req) {
+ const obj = new Parse.Object('AfterSaveProof');
+ obj.set('proof', req.object.id);
+ obj.save().then(test);
+ });
+
+ const obj = new Parse.Object('AfterSaveTest');
+ obj.save();
+
+ function test() {
+ const query = new Parse.Query('AfterSaveProof');
+ query.equalTo('proof', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results.length).toEqual(1);
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ }
+ });
+
+ it('test afterSave ran on created object and returned a promise', function (done) {
+ Parse.Cloud.afterSave('AfterSaveTest2', function (req) {
+ const obj = req.object;
+ if (!obj.existed()) {
+ return new Promise(resolve => {
+ setTimeout(function () {
+ obj.set('proof', obj.id);
+ obj.save().then(function () {
+ resolve();
+ });
+ }, 1000);
+ });
+ }
+ });
+
+ const obj = new Parse.Object('AfterSaveTest2');
+ obj.save().then(function () {
+ const query = new Parse.Query('AfterSaveTest2');
+ query.equalTo('proof', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results.length).toEqual(1);
+ const savedObject = results[0];
+ expect(savedObject.get('proof')).toEqual(obj.id);
done();
- }, function(error) {
+ },
+ function (error) {
fail(error);
done();
+ }
+ );
+ });
+ });
+
+ // TODO: Fails on CI randomly as racing
+ xit('test afterSave ignoring promise, object not found', function (done) {
+ Parse.Cloud.afterSave('AfterSaveTest2', function (req) {
+ const obj = req.object;
+ if (!obj.existed()) {
+ return new Promise(resolve => {
+ setTimeout(function () {
+ obj.set('proof', obj.id);
+ obj.save().then(function () {
+ resolve();
+ });
+ }, 1000);
});
}
});
+
+ const obj = new Parse.Object('AfterSaveTest2');
+ obj.save().then(function () {
+ done();
+ });
+
+ const query = new Parse.Query('AfterSaveTest2');
+ query.equalTo('proof', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results.length).toEqual(0);
+ },
+ function (error) {
+ fail(error);
+ }
+ );
+ });
+
+ it('test afterSave rejecting promise', function (done) {
+ Parse.Cloud.afterSave('AfterSaveTest2', function () {
+ return new Promise((resolve, reject) => {
+ setTimeout(function () {
+ reject('THIS SHOULD BE IGNORED');
+ }, 1000);
+ });
+ });
+
+ const obj = new Parse.Object('AfterSaveTest2');
+ obj.save().then(
+ function () {
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test afterDelete returning promise, object is deleted when destroy resolves', function (done) {
+ Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) {
+ return new Promise(resolve => {
+ setTimeout(function () {
+ const obj = new Parse.Object('AfterDeleteTestProof');
+ obj.set('proof', req.object.id);
+ obj.save().then(function () {
+ resolve();
+ });
+ }, 1000);
+ });
+ });
+
+ const errorHandler = function (error) {
+ fail(error);
+ done();
+ };
+
+ const obj = new Parse.Object('AfterDeleteTest2');
+ obj.save().then(function () {
+ obj.destroy().then(function () {
+ const query = new Parse.Query('AfterDeleteTestProof');
+ query.equalTo('proof', obj.id);
+ query.find().then(function (results) {
+ expect(results.length).toEqual(1);
+ const deletedObject = results[0];
+ expect(deletedObject.get('proof')).toEqual(obj.id);
+ done();
+ }, errorHandler);
+ }, errorHandler);
+ }, errorHandler);
+ });
+
+ it('test afterDelete ignoring promise, object is not yet deleted', function (done) {
+ Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) {
+ return new Promise(resolve => {
+ setTimeout(function () {
+ const obj = new Parse.Object('AfterDeleteTestProof');
+ obj.set('proof', req.object.id);
+ obj.save().then(function () {
+ resolve();
+ });
+ }, 1000);
+ });
+ });
+
+ const errorHandler = function (error) {
+ fail(error);
+ done();
+ };
+
+ const obj = new Parse.Object('AfterDeleteTest2');
+ obj.save().then(function () {
+ obj.destroy().then(function () {
+ done();
+ });
+
+ const query = new Parse.Query('AfterDeleteTestProof');
+ query.equalTo('proof', obj.id);
+ query.find().then(function (results) {
+ expect(results.length).toEqual(0);
+ }, errorHandler);
+ }, errorHandler);
+ });
+
+ it('test beforeSave happens on update', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('foo', 'baz');
+ });
+
+ const obj = new Parse.Object('BeforeSaveChanged');
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(function () {
+ obj.set('foo', 'bar');
+ return obj.save();
+ })
+ .then(
+ function () {
+ const query = new Parse.Query('BeforeSaveChanged');
+ return query.get(obj.id).then(function (objAgain) {
+ expect(objAgain.get('foo')).toEqual('baz');
+ done();
+ });
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test beforeDelete failure', function (done) {
+ Parse.Cloud.beforeDelete('BeforeDeleteFail', function () {
+ throw 'Nope';
+ });
+
+ const obj = new Parse.Object('BeforeDeleteFail');
+ let id;
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(() => {
+ id = obj.id;
+ return obj.destroy();
+ })
+ .then(
+ () => {
+ fail('obj.destroy() should have failed, but it succeeded');
+ done();
+ },
+ error => {
+ expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
+ expect(error.message).toEqual('Nope');
+
+ const objAgain = new Parse.Object('BeforeDeleteFail', {
+ objectId: id,
+ });
+ return objAgain.fetch();
+ }
+ )
+ .then(
+ objAgain => {
+ if (objAgain) {
+ expect(objAgain.get('foo')).toEqual('bar');
+ } else {
+ fail('unable to fetch the object ', id);
+ }
+ done();
+ },
+ error => {
+ // We should have been able to fetch the object again
+ fail(error);
+ }
+ );
+ });
+
+ it('basic beforeDelete rejection via promise', function (done) {
+ Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function () {
+ const query = new Parse.Query('Yolo');
+ return query.find().then(() => {
+ throw 'Nope';
+ });
+ });
+
+ const obj = new Parse.Object('BeforeDeleteFailWithPromise');
+ obj.set('foo', 'bar');
+ obj.save().then(
+ function () {
+ fail('Should not have been able to save BeforeSaveFailure class.');
+ done();
+ },
+ function (error) {
+ expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
+ expect(error.message).toEqual('Nope');
+
+ done();
+ }
+ );
+ });
+
+ it('test afterDelete ran and created an object', function (done) {
+ Parse.Cloud.afterDelete('AfterDeleteTest', function (req) {
+ const obj = new Parse.Object('AfterDeleteProof');
+ obj.set('proof', req.object.id);
+ obj.save().then(test);
+ });
+
+ const obj = new Parse.Object('AfterDeleteTest');
+ obj.save().then(function () {
+ obj.destroy();
+ });
+
+ function test() {
+ const query = new Parse.Query('AfterDeleteProof');
+ query.equalTo('proof', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results.length).toEqual(1);
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ }
+ });
+
+ it('test cloud function return types', function (done) {
+ Parse.Cloud.define('foo', function () {
+ return {
+ object: {
+ __type: 'Object',
+ className: 'Foo',
+ objectId: '123',
+ x: 2,
+ relation: {
+ __type: 'Object',
+ className: 'Bar',
+ objectId: '234',
+ x: 3,
+ },
+ },
+ array: [
+ {
+ __type: 'Object',
+ className: 'Bar',
+ objectId: '345',
+ x: 2,
+ },
+ ],
+ a: 2,
+ };
+ });
+
+ Parse.Cloud.run('foo').then(result => {
+ expect(result.object instanceof Parse.Object).toBeTruthy();
+ if (!result.object) {
+ fail('Unable to run foo');
+ done();
+ return;
+ }
+ expect(result.object.className).toEqual('Foo');
+ expect(result.object.get('x')).toEqual(2);
+ const bar = result.object.get('relation');
+ expect(bar instanceof Parse.Object).toBeTruthy();
+ expect(bar.className).toEqual('Bar');
+ expect(bar.get('x')).toEqual(3);
+ expect(Array.isArray(result.array)).toEqual(true);
+ expect(result.array[0] instanceof Parse.Object).toBeTruthy();
+ expect(result.array[0].get('x')).toEqual(2);
+ done();
+ });
+ });
+
+ it('test cloud function request params types', function (done) {
+ Parse.Cloud.define('params', function (req) {
+ expect(Utils.isDate(req.params.date)).toBe(true);
+ expect(req.params.date.getTime()).toBe(1463907600000);
+ expect(Utils.isDate(req.params.dateList[0])).toBe(true);
+ expect(req.params.dateList[0].getTime()).toBe(1463907600000);
+ expect(Utils.isDate(req.params.complexStructure.date[0])).toBe(true);
+ expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
+ expect(Utils.isDate(req.params.complexStructure.deepDate.date[0])).toBe(true);
+ expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
+ expect(Utils.isDate(req.params.complexStructure.deepDate2[0].date)).toBe(true);
+ expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
+ // Regression for #2294
+ expect(req.params.file instanceof Parse.File).toBe(true);
+ expect(req.params.file.url()).toEqual('https://some.url');
+ // Regression for #2204
+ expect(req.params.array).toEqual(['a', 'b', 'c']);
+ expect(Array.isArray(req.params.array)).toBe(true);
+ expect(req.params.arrayOfArray).toEqual([
+ ['a', 'b', 'c'],
+ ['d', 'e', 'f'],
+ ]);
+ expect(Array.isArray(req.params.arrayOfArray)).toBe(true);
+ expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true);
+ expect(Array.isArray(req.params.arrayOfArray[1])).toBe(true);
+ return {};
+ });
+
+ const params = {
+ date: {
+ __type: 'Date',
+ iso: '2016-05-22T09:00:00.000Z',
+ },
+ dateList: [
+ {
+ __type: 'Date',
+ iso: '2016-05-22T09:00:00.000Z',
+ },
+ ],
+ lol: 'hello',
+ complexStructure: {
+ date: [
+ {
+ __type: 'Date',
+ iso: '2016-05-22T09:00:00.000Z',
+ },
+ ],
+ deepDate: {
+ date: [
+ {
+ __type: 'Date',
+ iso: '2016-05-22T09:00:00.000Z',
+ },
+ ],
+ },
+ deepDate2: [
+ {
+ date: {
+ __type: 'Date',
+ iso: '2016-05-22T09:00:00.000Z',
+ },
+ },
+ ],
+ },
+ file: Parse.File.fromJSON({
+ __type: 'File',
+ name: 'name',
+ url: 'https://some.url',
+ }),
+ array: ['a', 'b', 'c'],
+ arrayOfArray: [
+ ['a', 'b', 'c'],
+ ['d', 'e', 'f'],
+ ],
+ };
+ Parse.Cloud.run('params', params).then(() => {
+ done();
+ });
+ });
+
+ it('test cloud function should echo keys', function (done) {
+ Parse.Cloud.define('echoKeys', function () {
+ return {
+ applicationId: Parse.applicationId,
+ masterKey: Parse.masterKey,
+ javascriptKey: Parse.javascriptKey,
+ };
+ });
+
+ Parse.Cloud.run('echoKeys').then(result => {
+ expect(result.applicationId).toEqual(Parse.applicationId);
+ expect(result.masterKey).toEqual(Parse.masterKey);
+ expect(result.javascriptKey).toEqual(Parse.javascriptKey);
+ done();
+ });
+ });
+
+ it('should properly create an object in before save', done => {
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('foo', 'baz');
+ });
+
+ Parse.Cloud.define('createBeforeSaveChangedObject', function () {
+ const obj = new Parse.Object('BeforeSaveChanged');
+ return obj.save().then(() => {
+ return obj;
+ });
+ });
+
+ Parse.Cloud.run('createBeforeSaveChangedObject').then(res => {
+ expect(res.get('foo')).toEqual('baz');
+ done();
+ });
+ });
+
+ it('dirtyKeys are set on update', done => {
+ let triggerTime = 0;
+ // Register a mock beforeSave hook
+ Parse.Cloud.beforeSave('GameScore', req => {
+ const object = req.object;
+ expect(object instanceof Parse.Object).toBeTruthy();
+ expect(object.get('fooAgain')).toEqual('barAgain');
+ if (triggerTime == 0) {
+ // Create
+ expect(object.get('foo')).toEqual('bar');
+ } else if (triggerTime == 1) {
+ // Update
+ expect(object.dirtyKeys()).toEqual(['foo']);
+ expect(object.dirty('foo')).toBeTruthy();
+ expect(object.get('foo')).toEqual('baz');
+ } else {
+ throw new Error();
+ }
+ triggerTime++;
+ });
+
+ const obj = new Parse.Object('GameScore');
+ obj.set('foo', 'bar');
+ obj.set('fooAgain', 'barAgain');
+ obj
+ .save()
+ .then(() => {
+ // We only update foo
+ obj.set('foo', 'baz');
+ return obj.save();
+ })
+ .then(
+ () => {
+ // Make sure the checking has been triggered
+ expect(triggerTime).toBe(2);
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test beforeSave unchanged success', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveUnchanged', function () {
+ return;
+ });
+
+ const obj = new Parse.Object('BeforeSaveUnchanged');
+ obj.set('foo', 'bar');
+ obj.save().then(
+ function () {
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test beforeDelete success', function (done) {
+ Parse.Cloud.beforeDelete('BeforeDeleteTest', function () {
+ return;
+ });
+
+ const obj = new Parse.Object('BeforeDeleteTest');
+ obj.set('foo', 'bar');
+ obj
+ .save()
+ .then(function () {
+ return obj.destroy();
+ })
+ .then(
+ function () {
+ const objAgain = new Parse.Object('BeforeDeleteTest', obj.id);
+ return objAgain.fetch().then(fail, () => done());
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('test save triggers get user', async done => {
+ Parse.Cloud.beforeSave('SaveTriggerUser', function (req) {
+ if (req.user && req.user.id) {
+ return;
+ } else {
+ throw new Error('No user present on request object for beforeSave.');
+ }
+ });
+
+ Parse.Cloud.afterSave('SaveTriggerUser', function (req) {
+ if (!req.user || !req.user.id) {
+ console.log('No user present on request object for afterSave.');
+ }
+ });
+
+ const user = new Parse.User();
+ user.set('password', 'asdf');
+ user.set('email', 'asdf@example.com');
+ user.set('username', 'zxcv');
+ await user.signUp();
+ const obj = new Parse.Object('SaveTriggerUser');
+ obj.save().then(
+ function () {
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('beforeSave change propagates through the save response', done => {
+ Parse.Cloud.beforeSave('ChangingObject', function (request) {
+ request.object.set('foo', 'baz');
+ });
+ const obj = new Parse.Object('ChangingObject');
+ obj.save({ foo: 'bar' }).then(
+ objAgain => {
+ expect(objAgain.get('foo')).toEqual('baz');
+ done();
+ },
+ () => {
+ fail('Should not have failed to save.');
+ done();
+ }
+ );
+ });
+
+ it('beforeSave change propagates through the afterSave #1931', done => {
+ Parse.Cloud.beforeSave('ChangingObject', function (request) {
+ request.object.unset('file');
+ request.object.unset('date');
+ });
+
+ Parse.Cloud.afterSave('ChangingObject', function (request) {
+ expect(request.object.has('file')).toBe(false);
+ expect(request.object.has('date')).toBe(false);
+ expect(request.object.get('file')).toBeUndefined();
+ return Promise.resolve();
+ });
+ const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain');
+ file
+ .save()
+ .then(() => {
+ const obj = new Parse.Object('ChangingObject');
+ return obj.save({ file, date: new Date() });
+ })
+ .then(
+ () => {
+ done();
+ },
+ () => {
+ fail();
+ done();
+ }
+ );
+ });
+
+ it('test cloud function parameter validation success', done => {
+ // Register a function with validation
+ Parse.Cloud.define(
+ 'functionWithParameterValidation',
+ () => {
+ return 'works';
+ },
+ request => {
+ return request.params.success === 100;
+ }
+ );
+
+ Parse.Cloud.run('functionWithParameterValidation', { success: 100 }).then(
+ () => {
+ done();
+ },
+ () => {
+ fail('Validation should not have failed.');
+ done();
+ }
+ );
+ });
+
+ it('doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)', done => {
+ Parse.Cloud.define('testQuery', function (request) {
+ return request.user.get('data');
+ });
+
+ Parse.User.signUp('user', 'pass')
+ .then(user => {
+ user.set('data', 'AAA');
+ return user.save();
+ })
+ .then(() => Parse.Cloud.run('testQuery'))
+ .then(result => {
+ expect(result).toEqual('AAA');
+ Parse.User.current().set('data', 'BBB');
+ return Parse.User.current().save(null, { useMasterKey: true });
+ })
+ .then(() => Parse.Cloud.run('testQuery'))
+ .then(result => {
+ expect(result).toEqual('BBB');
+ done();
+ });
+ });
+
+ it('clears out the user cache for all sessions when the user is changed', done => {
+ let session1;
+ let session2;
+ let user;
+ const cacheAdapter = new InMemoryCacheAdapter({ ttl: 100000000 });
+ reconfigureServer({ cacheAdapter })
+ .then(() => {
+ Parse.Cloud.define('checkStaleUser', request => {
+ return request.user.get('data');
+ });
+
+ user = new Parse.User();
+ user.set('username', 'test');
+ user.set('password', 'moon-y');
+ user.set('data', 'first data');
+ return user.signUp();
+ })
+ .then(user => {
+ session1 = user.getSessionToken();
+ return request({
+ url: 'http://localhost:8378/1/login?username=test&password=moon-y',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ });
+ })
+ .then(response => {
+ session2 = response.data.sessionToken;
+ //Ensure both session tokens are in the cache
+ return Parse.Cloud.run('checkStaleUser', { sessionToken: session2 });
+ })
+ .then(() =>
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/checkStaleUser',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': session2,
+ },
+ })
+ )
+ .then(() =>
+ Promise.all([
+ cacheAdapter.get('test:user:' + session1),
+ cacheAdapter.get('test:user:' + session2),
+ ])
+ )
+ .then(cachedVals => {
+ expect(cachedVals[0].objectId).toEqual(user.id);
+ expect(cachedVals[1].objectId).toEqual(user.id);
+
+ //Change with session 1 and then read with session 2.
+ user.set('data', 'second data');
+ return user.save();
+ })
+ .then(() =>
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/checkStaleUser',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': session2,
+ },
+ })
+ )
+ .then(response => {
+ expect(response.data.result).toEqual('second data');
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => {
+ Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => { });
+
+ const TestObject = Parse.Object.extend('TestObject');
+ const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');
+ const BeforeSaveObject = Parse.Object.extend('BeforeSaveUnchanged');
+
+ const aTestObject = new TestObject();
+ aTestObject.set('foo', 'bar');
+ aTestObject
+ .save()
+ .then(aTestObject => {
+ const aNoBeforeSaveObj = new NoBeforeSaveObject();
+ aNoBeforeSaveObj.set('aTestObject', aTestObject);
+ expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
+ return aNoBeforeSaveObj.save();
+ })
+ .then(aNoBeforeSaveObj => {
+ expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
+
+ const aBeforeSaveObj = new BeforeSaveObject();
+ aBeforeSaveObj.set('aTestObject', aTestObject);
+ expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
+ return aBeforeSaveObj.save();
+ })
+ .then(aBeforeSaveObj => {
+ expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar');
+ done();
+ });
+ });
+
+ it('should encode Parse Objects in cloud functions', async () => {
+ const user = new Parse.User();
+ user.setUsername('username');
+ user.setPassword('password');
+ user.set('deleted', false);
+ await user.signUp();
+ Parse.Cloud.define(
+ 'deleteAccount',
+ async req => {
+ expect(req.params.object instanceof Parse.Object).toBeTrue();
+ req.params.object.set('deleted', true);
+ await req.params.object.save(null, { useMasterKey: true });
+ return 'Object deleted';
+ },
+ {
+ requireMaster: true,
+ }
+ );
+ await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true });
+ });
+
+ it('beforeSave should not affect fetched pointers', done => {
+ Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => { });
+
+ Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) {
+ req.object.set('foo', 'baz');
+ });
+
+ const TestObject = Parse.Object.extend('TestObject');
+ const BeforeSaveUnchangedObject = Parse.Object.extend('BeforeSaveUnchanged');
+ const BeforeSaveChangedObject = Parse.Object.extend('BeforeSaveChanged');
+
+ const aTestObject = new TestObject();
+ aTestObject.set('foo', 'bar');
+ aTestObject
+ .save()
+ .then(aTestObject => {
+ const aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject();
+ aBeforeSaveUnchangedObject.set('aTestObject', aTestObject);
+ expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar');
+ return aBeforeSaveUnchangedObject.save();
+ })
+ .then(aBeforeSaveUnchangedObject => {
+ expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar');
+
+ const aBeforeSaveChangedObject = new BeforeSaveChangedObject();
+ aBeforeSaveChangedObject.set('aTestObject', aTestObject);
+ expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar');
+ return aBeforeSaveChangedObject.save();
+ })
+ .then(aBeforeSaveChangedObject => {
+ expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar');
+ expect(aBeforeSaveChangedObject.get('foo')).toEqual('baz');
+ done();
+ });
+ });
+
+ it('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => {
+ const TestObject = Parse.Object.extend('TestObject');
+ const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');
+ const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
+
+ Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
+ const object = req.object;
+ object.set('before', 'save');
+ });
+
+ Parse.Cloud.define('removeme', () => {
+ const testObject = new TestObject();
+ return testObject
+ .save()
+ .then(testObject => {
+ const object = new NoBeforeSaveObject({ remove: testObject });
+ return object.save();
+ })
+ .then(object => {
+ object.unset('remove');
+ return object.save();
+ });
+ });
+
+ Parse.Cloud.define('removeme2', () => {
+ const testObject = new TestObject();
+ return testObject
+ .save()
+ .then(testObject => {
+ const object = new BeforeSaveObject({ remove: testObject });
+ return object.save();
+ })
+ .then(object => {
+ object.unset('remove');
+ return object.save();
+ });
+ });
+
+ Parse.Cloud.run('removeme')
+ .then(aNoBeforeSaveObj => {
+ expect(aNoBeforeSaveObj.get('remove')).toEqual(undefined);
+
+ return Parse.Cloud.run('removeme2');
+ })
+ .then(aBeforeSaveObj => {
+ expect(aBeforeSaveObj.get('before')).toEqual('save');
+ expect(aBeforeSaveObj.get('remove')).toEqual(undefined);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ done();
+ });
+ });
+
+ /*
+ TODO: fix for Postgres
+ trying to delete a field that doesn't exists doesn't play nice
+ */
+ it_exclude_dbs(['postgres'])(
+ 'should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)',
+ done => {
+ const TestObject = Parse.Object.extend('TestObject');
+ const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
+
+ Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
+ const object = req.object;
+ object.set('before', 'save');
+ object.unset('remove');
+ });
+
+ let object;
+ const testObject = new TestObject({ key: 'value' });
+ testObject
+ .save()
+ .then(() => {
+ object = new BeforeSaveObject();
+ return object.save().then(() => {
+ object.set({ remove: testObject });
+ return object.save();
+ });
+ })
+ .then(objectAgain => {
+ expect(objectAgain.get('remove')).toBeUndefined();
+ expect(object.get('remove')).toBeUndefined();
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ done();
+ });
+ }
+ );
+
+ it('should not include relation op (regression test for #1606)', done => {
+ const TestObject = Parse.Object.extend('TestObject');
+ const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
+ let testObj;
+ Parse.Cloud.beforeSave('BeforeSaveChanged', req => {
+ const object = req.object;
+ object.set('before', 'save');
+ testObj = new TestObject();
+ return testObj.save().then(() => {
+ object.relation('testsRelation').add(testObj);
+ });
+ });
+
+ const object = new BeforeSaveObject();
+ object
+ .save()
+ .then(objectAgain => {
+ // Originally it would throw as it would be a non-relation
+ expect(() => {
+ objectAgain.relation('testsRelation');
+ }).not.toThrow();
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ done();
+ });
+ });
+
+ /**
+ * Checks that incrementing a value to a zero in a beforeSave hook
+ * does not result in that key being omitted from the response.
+ */
+ it('before save increment does not return undefined', done => {
+ Parse.Cloud.define('cloudIncrementClassFunction', function (req) {
+ const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass');
+ const obj = new CloudIncrementClass();
+ obj.id = req.params.objectId;
+ return obj.save();
+ });
+
+ Parse.Cloud.beforeSave('CloudIncrementClass', function (req) {
+ const obj = req.object;
+ if (!req.master) {
+ obj.increment('points', -10);
+ obj.increment('num', -9);
+ }
+ });
+
+ const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass');
+ const obj = new CloudIncrementClass();
+ obj.set('points', 10);
+ obj.set('num', 10);
+ obj.save(null, { useMasterKey: true }).then(function () {
+ Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then(function (
+ savedObj
+ ) {
+ expect(savedObj.get('num')).toEqual(1);
+ expect(savedObj.get('points')).toEqual(0);
+ done();
+ });
+ });
+ });
+
+ it('before save can revert fields', async () => {
+ Parse.Cloud.beforeSave('TestObject', ({ object }) => {
+ object.revert('foo');
+ return object;
+ });
+
+ Parse.Cloud.afterSave('TestObject', ({ object }) => {
+ expect(object.get('foo')).toBeUndefined();
+ return object;
+ });
+
+ const obj = new TestObject();
+ obj.set('foo', 'bar');
+ await obj.save();
+
+ expect(obj.get('foo')).toBeUndefined();
+ await obj.fetch();
+
+ expect(obj.get('foo')).toBeUndefined();
+ });
+
+ it('before save can revert fields with existing object', async () => {
+ Parse.Cloud.beforeSave(
+ 'TestObject',
+ ({ object }) => {
+ object.revert('foo');
+ return object;
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+
+ Parse.Cloud.afterSave(
+ 'TestObject',
+ ({ object }) => {
+ expect(object.get('foo')).toBe('bar');
+ return object;
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+
+ const obj = new TestObject();
+ obj.set('foo', 'bar');
+ await obj.save(null, { useMasterKey: true });
+
+ expect(obj.get('foo')).toBe('bar');
+ obj.set('foo', 'yolo');
+ await obj.save();
+ expect(obj.get('foo')).toBe('bar');
+ });
+
+ it('create role with name and ACL and a beforeSave', async () => {
+ Parse.Cloud.beforeSave(Parse.Role, ({ object }) => {
+ return object;
+ });
+
+ const obj = new Parse.Role('TestRole', new Parse.ACL({ '*': { read: true, write: true } }));
+ await obj.save();
+
+ expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } }));
+ expect(obj.get('name')).toEqual('TestRole');
+ await obj.fetch();
+
+ expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } }));
+ expect(obj.get('name')).toEqual('TestRole');
+ });
+
+ it('can unset in afterSave', async () => {
+ Parse.Cloud.beforeSave('TestObject', ({ object }) => {
+ if (!object.existed()) {
+ object.set('secret', true);
+ return object;
+ }
+ object.revert('secret');
+ });
+
+ Parse.Cloud.afterSave('TestObject', ({ object }) => {
+ object.unset('secret');
+ });
+
+ Parse.Cloud.beforeFind(
+ 'TestObject',
+ ({ query }) => {
+ query.exclude('secret');
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+
+ const obj = new TestObject();
+ await obj.save();
+ expect(obj.get('secret')).toBeUndefined();
+ await obj.fetch();
+ expect(obj.get('secret')).toBeUndefined();
+ await obj.fetch({ useMasterKey: true });
+ expect(obj.get('secret')).toBe(true);
+ });
+
+ it('should revert in beforeSave', async () => {
+ Parse.Cloud.beforeSave('MyObject', ({ object }) => {
+ if (!object.existed()) {
+ object.set('count', 0);
+ return object;
+ }
+ object.revert('count');
+ return object;
+ });
+ const obj = await new Parse.Object('MyObject').save();
+ expect(obj.get('count')).toBe(0);
+ obj.set('count', 10);
+ await obj.save();
+ expect(obj.get('count')).toBe(0);
+ await obj.fetch();
+ expect(obj.get('count')).toBe(0);
+ });
+
+ it('pointer should not be cleared by triggers', async () => {
+ Parse.Cloud.afterSave('MyObject', () => { });
+ const foo = await new Parse.Object('Test', { foo: 'bar' }).save();
+ const obj = await new Parse.Object('MyObject', { foo }).save();
+ const foo2 = obj.get('foo');
+ expect(foo2.get('foo')).toBe('bar');
+ });
+
+ it('can set a pointer in triggers', async () => {
+ Parse.Cloud.beforeSave('MyObject', () => { });
+ Parse.Cloud.afterSave(
+ 'MyObject',
+ async ({ object }) => {
+ const foo = await new Parse.Object('Test', { foo: 'bar' }).save();
+ object.set({ foo });
+ await object.save(null, { useMasterKey: true });
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const obj = await new Parse.Object('MyObject').save();
+ const foo2 = obj.get('foo');
+ expect(foo2.get('foo')).toBe('bar');
+ });
+
+ it('beforeSave should not sanitize database', async done => {
+ const { adapter } = Config.get(Parse.applicationId).database;
+ const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
+ spy.calls.saveArgumentsByValue();
+
+ let count = 0;
+ Parse.Cloud.beforeSave('CloudIncrementNested', req => {
+ count += 1;
+ req.object.set('foo', 'baz');
+ expect(typeof req.object.get('objectField').number).toBe('number');
+ });
+
+ Parse.Cloud.afterSave('CloudIncrementNested', req => {
+ expect(typeof req.object.get('objectField').number).toBe('number');
+ });
+
+ const obj = new Parse.Object('CloudIncrementNested');
+ obj.set('objectField', { number: 5 });
+ obj.set('foo', 'bar');
+ await obj.save();
+
+ obj.increment('objectField.number', 10);
+ await obj.save();
+
+ const [
+ ,
+ ,
+ ,
+ /* className */ /* schema */ /* query */ update,
+ ] = adapter.findOneAndUpdate.calls.first().args;
+ expect(update).toEqual({
+ 'objectField.number': { __op: 'Increment', amount: 10 },
+ foo: 'baz',
+ updatedAt: obj.updatedAt.toISOString(),
+ });
+
+ count === 2 ? done() : fail();
+ });
+
+ /**
+ * Verifies that an afterSave hook throwing an exception
+ * will not prevent a successful save response from being returned
+ */
+ it('should succeed on afterSave exception', done => {
+ Parse.Cloud.afterSave('AfterSaveTestClass', function () {
+ throw 'Exception';
+ });
+ const AfterSaveTestClass = Parse.Object.extend('AfterSaveTestClass');
+ const obj = new AfterSaveTestClass();
+ obj.save().then(done, done.fail);
+ });
+
+ describe('cloud jobs', () => {
+ it('should define a job', done => {
+ expect(() => {
+ Parse.Cloud.job('myJob', ({ message }) => {
+ message('Hello, world!!!');
+ });
+ }).not.toThrow();
+
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ },
+ })
+ .then(async response => {
+ const jobStatusId = response.headers['x-parse-job-status-id'];
+ const checkJobStatus = async () => {
+ const jobStatus = await getJobStatus(jobStatusId);
+ return jobStatus.get('finishedAt') && jobStatus.get('message') === 'Hello, world!!!';
+ };
+ while (!(await checkJobStatus())) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should not run without master key', done => {
+ expect(() => {
+ Parse.Cloud.job('myJob', () => { });
+ }).not.toThrow();
+
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ }).then(
+ () => {
+ fail('Expected to be unauthorized');
+ done();
+ },
+ err => {
+ expect(err.status).toBe(403);
+ done();
+ }
+ );
+ });
+
+ it('should run with master key', done => {
+ expect(() => {
+ Parse.Cloud.job('myJob', (req, res) => {
+ expect(req.functionName).toBeUndefined();
+ expect(req.jobName).toBe('myJob');
+ expect(typeof req.jobId).toBe('string');
+ expect(typeof req.message).toBe('function');
+ expect(typeof res).toBe('undefined');
+ });
+ }).not.toThrow();
+
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ },
+ })
+ .then(async response => {
+ const jobStatusId = response.headers['x-parse-job-status-id'];
+ const checkJobStatus = async () => {
+ const jobStatus = await getJobStatus(jobStatusId);
+ return jobStatus.get('finishedAt');
+ };
+ while (!(await checkJobStatus())) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should run with master key basic auth', done => {
+ expect(() => {
+ Parse.Cloud.job('myJob', (req, res) => {
+ expect(req.functionName).toBeUndefined();
+ expect(req.jobName).toBe('myJob');
+ expect(typeof req.jobId).toBe('string');
+ expect(typeof req.message).toBe('function');
+ expect(typeof res).toBe('undefined');
+ });
+ }).not.toThrow();
+
+ request({
+ method: 'POST',
+ url: `http://${Parse.applicationId}:${Parse.masterKey}@localhost:8378/1/jobs/myJob`,
+ })
+ .then(async response => {
+ const jobStatusId = response.headers['x-parse-job-status-id'];
+ const checkJobStatus = async () => {
+ const jobStatus = await getJobStatus(jobStatusId);
+ return jobStatus.get('finishedAt');
+ };
+ while (!(await checkJobStatus())) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should set the message / success on the job', done => {
+ Parse.Cloud.job('myJob', req => {
+ return req
+ .message('hello')
+ .then(() => {
+ return getJobStatus(req.jobId);
+ })
+ .then(jobStatus => {
+ expect(jobStatus.get('message')).toEqual('hello');
+ expect(jobStatus.get('status')).toEqual('running');
+ });
+ });
+
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ },
+ })
+ .then(async response => {
+ const jobStatusId = response.headers['x-parse-job-status-id'];
+ const checkJobStatus = async () => {
+ const jobStatus = await getJobStatus(jobStatusId);
+ return (
+ jobStatus.get('finishedAt') &&
+ jobStatus.get('message') === 'hello' &&
+ jobStatus.get('status') === 'succeeded'
+ );
+ };
+ while (!(await checkJobStatus())) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should set the failure on the job', done => {
+ Parse.Cloud.job('myJob', () => {
+ return Promise.reject('Something went wrong');
+ });
+
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ },
+ })
+ .then(async response => {
+ const jobStatusId = response.headers['x-parse-job-status-id'];
+ const checkJobStatus = async () => {
+ const jobStatus = await getJobStatus(jobStatusId);
+ return (
+ jobStatus.get('finishedAt') &&
+ jobStatus.get('message') === 'Something went wrong' &&
+ jobStatus.get('status') === 'failed'
+ );
+ };
+ while (!(await checkJobStatus())) {
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should set the failure message on the job error', async () => {
+ Parse.Cloud.job('myJobError', () => {
+ throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Something went wrong');
+ });
+ const job = await Parse.Cloud.startJob('myJobError');
+ let jobStatus, status;
+ while (status !== 'failed') {
+ if (jobStatus) {
+ await new Promise(resolve => setTimeout(resolve, 10));
+ }
+ jobStatus = await Parse.Cloud.getJobStatus(job);
+ status = jobStatus.get('status');
+ }
+ expect(jobStatus.get('message')).toEqual('Something went wrong');
+ });
+
+ function getJobStatus(jobId) {
+ const q = new Parse.Query('_JobStatus');
+ return q.get(jobId, { useMasterKey: true });
+ }
+ });
+});
+
+describe('cloud functions', () => {
+ it('Should have request ip', done => {
+ Parse.Cloud.define('myFunction', req => {
+ expect(req.ip).toBeDefined();
+ return 'success';
+ });
+
+ Parse.Cloud.run('myFunction', {}).then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.define('myConfigFunction', req => {
+ expect(req.config).toBeDefined();
+ return 'success';
+ });
+ await Parse.Cloud.run('myConfigFunction', {});
+ });
+});
+
+describe('beforeSave hooks', () => {
+ it('should have request headers', done => {
+ Parse.Cloud.beforeSave('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject.save().then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.beforeSave('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject.save().then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.beforeSave('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ });
+
+ it('should respect custom object ids (#6733)', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.object.id).toEqual('test_6733');
+ });
+
+ await reconfigureServer({ allowCustomObjectId: true });
+
+ const req = request({
+ // Parse JS SDK does not currently support custom object ids (see #1097), so we do a REST request
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ objectId: 'test_6733',
+ foo: 'bar',
+ },
+ });
+
+ {
+ const res = await req;
+ expect(res.data.objectId).toEqual('test_6733');
+ }
+
+ const query = new Parse.Query('TestObject');
+ query.equalTo('objectId', 'test_6733');
+ const res = await query.find();
+ expect(res.length).toEqual(1);
+ expect(res[0].get('foo')).toEqual('bar');
+ });
+
+ it('should have access to plaintext password on signup for password policy enforcement', async () => {
+ let receivedPassword;
+ Parse.Cloud.beforeSave(Parse.User, req => {
+ receivedPassword = req.object.get('password');
+ });
+
+ const user = new Parse.User();
+ user.setUsername('testuser');
+ user.setPassword('securePassword123');
+ await user.signUp();
+
+ expect(receivedPassword).toBe('securePassword123');
+ });
+
+ it('should have access to plaintext password on password change for password policy enforcement', async () => {
+ const user = new Parse.User();
+ user.setUsername('testuser');
+ user.setPassword('originalPassword');
+ await user.signUp();
+
+ let receivedPassword;
+ Parse.Cloud.beforeSave(Parse.User, req => {
+ receivedPassword = req.object.get('password');
+ });
+
+ user.setPassword('newPassword456');
+ await user.save(null, { sessionToken: user.getSessionToken() });
+
+ expect(receivedPassword).toBe('newPassword456');
+ });
+
+ it('should not expose plaintext password in API response', async () => {
+ Parse.Cloud.beforeSave(Parse.User, () => {});
+
+ const user = new Parse.User();
+ user.setUsername('testuser');
+ user.setPassword('securePassword123');
+ const result = await user.signUp();
+
+ expect(result.get('password')).toBeUndefined();
+ expect(result.get('_hashed_password')).toBeUndefined();
+ });
+});
+
+describe('afterSave hooks', () => {
+ it('should have request headers', done => {
+ Parse.Cloud.afterSave('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject.save().then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.afterSave('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject.save().then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.afterSave('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ });
+
+ it('should unset in afterSave', async () => {
+ Parse.Cloud.afterSave(
+ 'MyObject',
+ ({ object }) => {
+ object.unset('secret');
+ },
+ {
+ skipWithMasterKey: true,
+ }
+ );
+ const obj = new Parse.Object('MyObject');
+ obj.set('secret', 'bar');
+ await obj.save();
+ expect(obj.get('secret')).toBeUndefined();
+ await obj.fetch();
+ expect(obj.get('secret')).toBe('bar');
+ });
+
+ it('should unset', async () => {
+ Parse.Cloud.beforeSave('MyObject', ({ object }) => {
+ object.set('secret', 'hidden');
+ });
+
+ Parse.Cloud.afterSave('MyObject', ({ object }) => {
+ object.unset('secret');
+ });
+ const obj = await new Parse.Object('MyObject').save();
+ expect(obj.get('secret')).toBeUndefined();
+ });
+});
+
+describe('beforeDelete hooks', () => {
+ it('should have request headers', done => {
+ Parse.Cloud.beforeDelete('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => myObj.destroy())
+ .then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.beforeDelete('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => myObj.destroy())
+ .then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.beforeDelete('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ await myObject.destroy();
+ });
+});
+
+describe('afterDelete hooks', () => {
+ it('should have request headers', done => {
+ Parse.Cloud.afterDelete('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => myObj.destroy())
+ .then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.afterDelete('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => myObj.destroy())
+ .then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.afterDelete('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ await myObject.destroy();
+ });
+});
+
+describe('beforeFind hooks', () => {
+ it('should add beforeFind trigger', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const q = req.query;
+ expect(q instanceof Parse.Query).toBe(true);
+ const jsonQuery = q.toJSON();
+ expect(jsonQuery.where.key).toEqual('value');
+ expect(jsonQuery.where.some).toEqual({ $gt: 10 });
+ expect(jsonQuery.include).toEqual('otherKey,otherValue');
+ expect(jsonQuery.excludeKeys).toBe('exclude');
+ expect(jsonQuery.limit).toEqual(100);
+ expect(jsonQuery.skip).toBe(undefined);
+ expect(jsonQuery.order).toBe('key');
+ expect(jsonQuery.keys).toBe('select');
+ expect(jsonQuery.readPreference).toBe('PRIMARY');
+ expect(jsonQuery.includeReadPreference).toBe('SECONDARY');
+ expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED');
+
+ expect(req.isGet).toEqual(false);
+ });
+
+ const query = new Parse.Query('MyObject');
+ query.equalTo('key', 'value');
+ query.greaterThan('some', 10);
+ query.include('otherKey');
+ query.include('otherValue');
+ query.ascending('key');
+ query.select('select');
+ query.exclude('exclude');
+ query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED');
+ query.find().then(() => {
+ done();
+ });
+ });
+
+ it('should use modify', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const q = req.query;
+ q.equalTo('forced', true);
+ });
+
+ const obj0 = new Parse.Object('MyObject');
+ obj0.set('forced', false);
+
+ const obj1 = new Parse.Object('MyObject');
+ obj1.set('forced', true);
+ Parse.Object.saveAll([obj0, obj1]).then(() => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('forced', false);
+ query.find().then(results => {
+ expect(results.length).toBe(1);
+ const firstResult = results[0];
+ expect(firstResult.get('forced')).toBe(true);
+ done();
+ });
+ });
+ });
+
+ it('should use the modified the query', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const q = req.query;
+ const otherQuery = new Parse.Query('MyObject');
+ otherQuery.equalTo('forced', true);
+ return Parse.Query.or(q, otherQuery);
+ });
+
+ const obj0 = new Parse.Object('MyObject');
+ obj0.set('forced', false);
+
+ const obj1 = new Parse.Object('MyObject');
+ obj1.set('forced', true);
+ Parse.Object.saveAll([obj0, obj1]).then(() => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('forced', false);
+ query.find().then(results => {
+ expect(results.length).toBe(2);
+ done();
+ });
+ });
+ });
+
+ it('should have object found with nested relational data query', async () => {
+ const obj1 = Parse.Object.extend('TestObject');
+ const obj2 = Parse.Object.extend('TestObject2');
+ let item2 = new obj2();
+ item2 = await item2.save();
+ let item1 = new obj1();
+ const relation = item1.relation('rel');
+ relation.add(item2);
+ item1 = await item1.save();
+ Parse.Cloud.beforeFind('TestObject', req => {
+ const additionalQ = new Parse.Query('TestObject');
+ additionalQ.equalTo('rel', item2);
+ return Parse.Query.and(req.query, additionalQ);
+ });
+ const q = new Parse.Query('TestObject');
+ const res = await q.first();
+ expect(res.id).toEqual(item1.id);
+ });
+
+ it('should use the modified exclude query', async () => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const q = req.query;
+ q.exclude('number');
+ });
+
+ const obj = new Parse.Object('MyObject');
+ obj.set('number', 100);
+ obj.set('string', 'hello');
+ await obj.save();
+
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ const results = await query.find();
+ expect(results.length).toBe(1);
+ expect(results[0].get('number')).toBeUndefined();
+ expect(results[0].get('string')).toBe('hello');
+ });
+
+ it('should reject queries', done => {
+ Parse.Cloud.beforeFind('MyObject', () => {
+ return Promise.reject('Do not run that query');
+ });
+
+ const query = new Parse.Query('MyObject');
+ query.find().then(
+ () => {
+ fail('should not succeed');
+ done();
+ },
+ err => {
+ expect(err.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(err.message).toEqual('Do not run that query');
+ done();
+ }
+ );
+ });
+
+ it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)('should handle empty where', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const otherQuery = new Parse.Query('MyObject');
+ otherQuery.equalTo('some', true);
+ return Parse.Query.or(req.query, otherQuery);
+ });
+
+ request({
+ url: 'http://localhost:8378/1/classes/MyObject',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ }).then(
+ () => {
+ done();
+ },
+ err => {
+ fail(err);
+ done();
+ }
+ );
+ });
+
+ it('should handle sorting where', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ const query = req.query;
+ query.ascending('score');
+ return query;
+ });
+
+ const count = 20;
+ const objects = [];
+ while (objects.length != count) {
+ const object = new Parse.Object('MyObject');
+ object.set('score', Math.floor(Math.random() * 100));
+ objects.push(object);
+ }
+ Parse.Object.saveAll(objects)
+ .then(() => {
+ const query = new Parse.Query('MyObject');
+ return query.find();
+ })
+ .then(objects => {
+ let lastScore = -1;
+ objects.forEach(element => {
+ expect(element.get('score') >= lastScore).toBe(true);
+ lastScore = element.get('score');
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should add beforeFind trigger using get API', done => {
+ const hook = {
+ method: function (req) {
+ expect(req.isGet).toEqual(true);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ obj.set('secretField', 'SSID');
+ obj.save().then(function () {
+ request({
+ method: 'GET',
+ url: 'http://localhost:8378/1/classes/MyObject/' + obj.id,
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ }).then(response => {
+ const body = response.data;
+ expect(body.secretField).toEqual('SSID');
+ expect(hook.method).toHaveBeenCalled();
+ done();
+ });
+ });
+ });
+
+ it('sets correct beforeFind trigger isGet parameter for Parse.Object.fetch request', async () => {
+ const hook = {
+ method: req => {
+ expect(req.isGet).toEqual(true);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ const getObj = await obj.fetch();
+ expect(getObj).toBeInstanceOf(Parse.Object);
+ expect(hook.method).toHaveBeenCalledTimes(1);
+ });
+
+ it('sets correct beforeFind trigger isGet parameter for Parse.Query.get request', async () => {
+ const hook = {
+ method: req => {
+ expect(req.isGet).toEqual(false);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ const query = new Parse.Query('MyObject');
+ const getObj = await query.get(obj.id);
+ expect(getObj).toBeInstanceOf(Parse.Object);
+ expect(hook.method).toHaveBeenCalledTimes(1);
+ });
+
+ it('sets correct beforeFind trigger isGet parameter for Parse.Query.find request', async () => {
+ const hook = {
+ method: req => {
+ expect(req.isGet).toEqual(false);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ const query = new Parse.Query('MyObject');
+ const findObjs = await query.find();
+ expect(findObjs?.[0]).toBeInstanceOf(Parse.Object);
+ expect(hook.method).toHaveBeenCalledTimes(1);
+ });
+
+ it('should have request headers', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObj.id);
+ return Promise.all([query.get(myObj.id), query.first(), query.find()]);
+ })
+ .then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObj.id);
+ return Promise.all([query.get(myObj.id), query.first(), query.find()]);
+ })
+ .then(() => done());
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObject.id);
+ await Promise.all([query.get(myObject.id), query.first(), query.find()]);
+ })
+ it('should run beforeFind on pointers and array of pointers from an object', async () => {
+ const obj1 = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject2');
+ const obj3 = new Parse.Object('TestObject');
+ obj2.set('aField', 'aFieldValue');
+ await obj2.save();
+ obj1.set('pointerField', obj2);
+ obj3.set('pointerFieldArray', [obj2]);
+ await obj1.save();
+ await obj3.save();
+ const spy = jasmine.createSpy('beforeFindSpy');
+ Parse.Cloud.beforeFind('TestObject2', spy);
+ const query = new Parse.Query('TestObject');
+ await query.get(obj1.id);
+ // Pointer not included in query so we don't expect beforeFind to be called
+ expect(spy).not.toHaveBeenCalled();
+ const query2 = new Parse.Query('TestObject');
+ query2.include('pointerField');
+ const res = await query2.get(obj1.id);
+ expect(res.get('pointerField').get('aField')).toBe('aFieldValue');
+ // Pointer included in query so we expect beforeFind to be called
+ expect(spy).toHaveBeenCalledTimes(1);
+ const query3 = new Parse.Query('TestObject');
+ query3.include('pointerFieldArray');
+ const res2 = await query3.get(obj3.id);
+ expect(res2.get('pointerFieldArray')[0].get('aField')).toBe('aFieldValue');
+ expect(spy).toHaveBeenCalledTimes(2);
+ });
+
+ it('should have access to context in include query in beforeFind hook', async () => {
+ let beforeFindTestObjectCalled = false;
+ let beforeFindTestObject2Called = false;
+ const obj1 = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject2');
+ obj2.set('aField', 'aFieldValue');
+ await obj2.save();
+ obj1.set('pointerField', obj2);
+ await obj1.save();
+ Parse.Cloud.beforeFind('TestObject', req => {
+ expect(req.context).toBeDefined();
+ expect(req.context.a).toEqual('a');
+ beforeFindTestObjectCalled = true;
+ });
+ Parse.Cloud.beforeFind('TestObject2', req => {
+ expect(req.context).toBeDefined();
+ expect(req.context.a).toEqual('a');
+ beforeFindTestObject2Called = true;
+ });
+ const query = new Parse.Query('TestObject');
+ await query.include('pointerField').find({ context: { a: 'a' } });
+ expect(beforeFindTestObjectCalled).toBeTrue();
+ expect(beforeFindTestObject2Called).toBeTrue();
+ });
+});
+
+describe('afterFind hooks', () => {
+ it('should add afterFind trigger', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ const q = req.query;
+ expect(q instanceof Parse.Query).toBe(true);
+ const jsonQuery = q.toJSON();
+ expect(jsonQuery.where.key).toEqual('value');
+ expect(jsonQuery.where.some).toEqual({ $gt: 10 });
+ expect(jsonQuery.include).toEqual('otherKey,otherValue');
+ expect(jsonQuery.excludeKeys).toBe('exclude');
+ expect(jsonQuery.limit).toEqual(100);
+ expect(jsonQuery.skip).toBe(undefined);
+ expect(jsonQuery.order).toBe('key');
+ expect(jsonQuery.keys).toBe('select');
+ expect(jsonQuery.readPreference).toBe('PRIMARY');
+ expect(jsonQuery.includeReadPreference).toBe('SECONDARY');
+ expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED');
+ });
+
+ const query = new Parse.Query('MyObject');
+ query.equalTo('key', 'value');
+ query.greaterThan('some', 10);
+ query.include('otherKey');
+ query.include('otherValue');
+ query.ascending('key');
+ query.select('select');
+ query.exclude('exclude');
+ query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED');
+ query.find().then(() => {
+ done();
+ });
+ });
+ it('should add afterFind trigger using get', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ for (let i = 0; i < req.objects.length; i++) {
+ req.objects[i].set('secretField', '###');
+ }
+ return req.objects;
+ });
+ const obj = new Parse.Object('MyObject');
+ obj.set('secretField', 'SSID');
+ obj.save().then(
+ function () {
+ const query = new Parse.Query('MyObject');
+ query.get(obj.id).then(
+ function (result) {
+ expect(result.get('secretField')).toEqual('###');
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('should add afterFind trigger using find', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ for (let i = 0; i < req.objects.length; i++) {
+ req.objects[i].set('secretField', '###');
+ }
+ return req.objects;
+ });
+ const obj = new Parse.Object('MyObject');
+ obj.set('secretField', 'SSID');
+ obj.save().then(
+ function () {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results[0].get('secretField')).toEqual('###');
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('should filter out results', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ const filteredResults = [];
+ for (let i = 0; i < req.objects.length; i++) {
+ if (req.objects[i].get('secretField') === 'SSID1') {
+ filteredResults.push(req.objects[i]);
+ }
+ }
+ return filteredResults;
+ });
+ const obj0 = new Parse.Object('MyObject');
+ obj0.set('secretField', 'SSID1');
+ const obj1 = new Parse.Object('MyObject');
+ obj1.set('secretField', 'SSID2');
+ Parse.Object.saveAll([obj0, obj1]).then(
+ function () {
+ const query = new Parse.Query('MyObject');
+ query.find().then(
+ function (results) {
+ expect(results[0].get('secretField')).toEqual('SSID1');
+ expect(results.length).toEqual(1);
+ done();
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ },
+ function (error) {
+ fail(error);
+ done();
+ }
+ );
+ });
+
+ it('should handle failures', done => {
+ Parse.Cloud.afterFind('MyObject', () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
+ });
+ const obj = new Parse.Object('MyObject');
+ obj.set('secretField', 'SSID');
+ obj.save().then(
+ function () {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ query.find().then(
+ function () {
+ fail('AfterFind should handle response failure correctly');
+ done();
+ },
+ function () {
+ done();
+ }
+ );
+ },
+ function () {
+ done();
+ }
+ );
+ });
+
+ it('should also work with promise', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ return new Promise(resolve => {
+ setTimeout(function () {
+ for (let i = 0; i < req.objects.length; i++) {
+ req.objects[i].set('secretField', '###');
+ }
+ resolve(req.objects);
+ }, 1000);
+ });
+ });
+ const obj = new Parse.Object('MyObject');
+ obj.set('secretField', 'SSID');
+ obj.save().then(
+ function () {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ query.find().then(
+ function (results) {
+ expect(results[0].get('secretField')).toEqual('###');
+ done();
+ },
+ function (error) {
+ fail(error);
+ }
+ );
+ },
+ function (error) {
+ fail(error);
+ }
+ );
+ });
+
+ it('should alter select', done => {
+ Parse.Cloud.beforeFind('MyObject', req => {
+ req.query.select('white');
+ return req.query;
+ });
+
+ const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true);
+ obj0.save().then(() => {
+ new Parse.Query('MyObject').first().then(result => {
+ expect(result.get('white')).toBe(true);
+ expect(result.get('black')).toBe(undefined);
+ done();
+ });
+ });
+ });
+
+ it('should not alter select', done => {
+ const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true);
+ obj0.save().then(() => {
+ new Parse.Query('MyObject').first().then(result => {
+ expect(result.get('white')).toBe(true);
+ expect(result.get('black')).toBe(true);
+ done();
+ });
+ });
+ });
+
+ it('should set count to true on beforeFind hooks if query is count', done => {
+ const hook = {
+ method: function (req) {
+ expect(req.count).toBe(true);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('Stuff', hook.method);
+ new Parse.Query('Stuff').count().then(count => {
+ expect(count).toBe(0);
+ expect(hook.method).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('should set count to false on beforeFind hooks if query is not count', done => {
+ const hook = {
+ method: function (req) {
+ expect(req.count).toBe(false);
+ return Promise.resolve();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.beforeFind('Stuff', hook.method);
+ new Parse.Query('Stuff').find().then(res => {
+ expect(res.length).toBe(0);
+ expect(hook.method).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('can set a pointer object in afterFind', async () => {
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ Parse.Cloud.afterFind('MyObject', async ({ objects }) => {
+ const otherObject = new Parse.Object('Test');
+ otherObject.set('foo', 'bar');
+ await otherObject.save();
+ objects[0].set('Pointer', otherObject);
+ objects[0].set('xyz', 'yolo');
+ expect(objects[0].get('Pointer').get('foo')).toBe('bar');
+ });
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ const obj2 = await query.first();
+ expect(obj2.get('xyz')).toBe('yolo');
+ const pointer = obj2.get('Pointer');
+ expect(pointer.get('foo')).toBe('bar');
+ });
+
+ it('can set invalid object in afterFind', async () => {
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ Parse.Cloud.afterFind('MyObject', () => [{}]);
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', obj.id);
+ const obj2 = await query.first();
+ expect(obj2).toBeDefined();
+ expect(obj2.toJSON()).toEqual({});
+ expect(obj2.id).toBeUndefined();
+ });
+
+ it('can return a unsaved object in afterFind', async () => {
+ const obj = new Parse.Object('MyObject');
+ await obj.save();
+ Parse.Cloud.afterFind('MyObject', async () => {
+ const otherObject = new Parse.Object('Test');
+ otherObject.set('foo', 'bar');
+ return [otherObject];
+ });
+ const query = new Parse.Query('MyObject');
+ const obj2 = await query.first();
+ expect(obj2.get('foo')).toEqual('bar');
+ expect(obj2.id).toBeUndefined();
+ await obj2.save();
+ expect(obj2.id).toBeDefined();
+ });
+
+ it('should have request headers', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ expect(req.headers).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObj.id);
+ return Promise.all([query.get(myObj.id), query.first(), query.find()]);
+ })
+ .then(() => done());
+ });
+
+ it('should have request ip', done => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ expect(req.ip).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ myObject
+ .save()
+ .then(myObj => {
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObj.id);
+ return Promise.all([query.get(myObj.id), query.first(), query.find()]);
+ })
+ .then(() => done())
+ .catch(done.fail);
+ });
+
+ it('should have request config', async () => {
+ Parse.Cloud.afterFind('MyObject', req => {
+ expect(req.config).toBeDefined();
+ });
+
+ const MyObject = Parse.Object.extend('MyObject');
+ const myObject = new MyObject();
+ await myObject.save();
+ const query = new Parse.Query('MyObject');
+ query.equalTo('objectId', myObject.id);
+ await Promise.all([query.get(myObject.id), query.first(), query.find()]);
+ });
+
+ it('should validate triggers correctly', () => {
+ expect(() => {
+ Parse.Cloud.beforeSave('_Session', () => { });
+ }).toThrow('Only the afterLogout trigger is allowed for the _Session class.');
+ expect(() => {
+ Parse.Cloud.afterSave('_Session', () => { });
+ }).toThrow('Only the afterLogout trigger is allowed for the _Session class.');
+ expect(() => {
+ Parse.Cloud.beforeSave('_PushStatus', () => { });
+ }).toThrow('Only afterSave is allowed on _PushStatus');
+ expect(() => {
+ Parse.Cloud.afterSave('_PushStatus', () => { });
+ }).not.toThrow();
+ expect(() => {
+ Parse.Cloud.beforeLogin(() => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
+ expect(() => {
+ Parse.Cloud.beforeLogin('_User', () => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
+ expect(() => {
+ Parse.Cloud.beforeLogin(Parse.User, () => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers');
+ expect(() => {
+ Parse.Cloud.beforeLogin('SomeClass', () => { });
+ }).toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.afterLogin(() => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.afterLogin('_User', () => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.afterLogin(Parse.User, () => { });
+ }).not.toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.afterLogin('SomeClass', () => { });
+ }).toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.afterLogout(() => { });
+ }).not.toThrow();
+ expect(() => {
+ Parse.Cloud.afterLogout('_Session', () => { });
+ }).not.toThrow();
+ expect(() => {
+ Parse.Cloud.afterLogout('_User', () => { });
+ }).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
+ expect(() => {
+ Parse.Cloud.afterLogout('SomeClass', () => { });
+ }).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
+ });
+
+ it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)('should skip afterFind hooks for aggregate', done => {
+ const hook = {
+ method: function () {
+ return Promise.reject();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.afterFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ const pipeline = [
+ {
+ $group: { _id: {} },
+ },
+ ];
+ obj
+ .save()
+ .then(() => {
+ const query = new Parse.Query('MyObject');
+ return query.aggregate(pipeline);
+ })
+ .then(results => {
+ expect(results[0].objectId).toEqual(null);
+ expect(hook.method).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)('should skip afterFind hooks for distinct', done => {
+ const hook = {
+ method: function () {
+ return Promise.reject();
+ },
+ };
+ spyOn(hook, 'method').and.callThrough();
+ Parse.Cloud.afterFind('MyObject', hook.method);
+ const obj = new Parse.Object('MyObject');
+ obj.set('score', 10);
+ obj
+ .save()
+ .then(() => {
+ const query = new Parse.Query('MyObject');
+ return query.distinct('score');
+ })
+ .then(results => {
+ expect(results[0]).toEqual(10);
+ expect(hook.method).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('should throw error if context header is malformed', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', () => {
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', () => {
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': 'key',
+ },
+ body: {
+ foo: 'bar',
+ },
+ });
+ try {
+ await req;
+ fail('Should have thrown error');
+ } catch (e) {
+ expect(e).toBeDefined();
+ expect(e.data.code).toEqual(Parse.Error.INVALID_JSON);
+ }
+ expect(calledBefore).toBe(false);
+ expect(calledAfter).toBe(false);
+ });
+
+ it('should throw error if context header is string "1"', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', () => {
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', () => {
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '1',
+ },
+ body: {
+ foo: 'bar',
+ },
+ });
+ try {
+ await req;
+ fail('Should have thrown error');
+ } catch (e) {
+ expect(e).toBeDefined();
+ expect(e.data.code).toEqual(Parse.Error.INVALID_JSON);
+ }
+ expect(calledBefore).toBe(false);
+ expect(calledAfter).toBe(false);
+ });
+
+ it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)('should expose context in beforeSave/afterSave via header', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.object.get('foo')).toEqual('bar');
+ expect(req.context.otherKey).toBe(1);
+ expect(req.context.key).toBe('value');
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.object.get('foo')).toEqual('bar');
+ expect(req.context.otherKey).toBe(1);
+ expect(req.context.key).toBe('value');
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}',
+ },
+ body: {
+ foo: 'bar',
+ },
+ });
+ await req;
+ expect(calledBefore).toBe(true);
+ expect(calledAfter).toBe(true);
+ });
+
+ it('should override header context with body context in beforeSave/afterSave', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.object.get('foo')).toEqual('bar');
+ expect(req.context.otherKey).toBe(10);
+ expect(req.context.key).toBe('hello');
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.object.get('foo')).toEqual('bar');
+ expect(req.context.otherKey).toBe(10);
+ expect(req.context.key).toBe('hello');
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}',
+ },
+ body: {
+ foo: 'bar',
+ _ApplicationId: 'test',
+ _context: '{"key":"hello","otherKey":10}',
+ },
+ });
+ await req;
+ expect(calledBefore).toBe(true);
+ expect(calledAfter).toBe(true);
+ });
+
+ it('should throw error if context body is malformed', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', () => {
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', () => {
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}',
+ },
+ body: {
+ foo: 'bar',
+ _ApplicationId: 'test',
+ _context: 'key',
+ },
+ });
+ try {
+ await req;
+ fail('Should have thrown error');
+ } catch (e) {
+ expect(e).toBeDefined();
+ expect(e.data.code).toEqual(Parse.Error.INVALID_JSON);
+ }
+ expect(calledBefore).toBe(false);
+ expect(calledAfter).toBe(false);
+ });
+
+ it('should throw error if context body is string "true"', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('TestObject', () => {
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('TestObject', () => {
+ calledAfter = true;
+ });
+ const req = request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}',
+ },
+ body: {
+ foo: 'bar',
+ _ApplicationId: 'test',
+ _context: 'true',
+ },
+ });
+ try {
+ await req;
+ fail('Should have thrown error');
+ } catch (e) {
+ expect(e).toBeDefined();
+ expect(e.data.code).toEqual(Parse.Error.INVALID_JSON);
+ }
+ expect(calledBefore).toBe(false);
+ expect(calledAfter).toBe(false);
+ });
+
+ it('should expose context in before and afterSave', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('MyClass', req => {
+ req.context = {
+ key: 'value',
+ otherKey: 1,
+ };
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('MyClass', req => {
+ expect(req.context.otherKey).toBe(1);
+ expect(req.context.key).toBe('value');
+ calledAfter = true;
+ });
+
+ const object = new Parse.Object('MyClass');
+ await object.save();
+ expect(calledBefore).toBe(true);
+ expect(calledAfter).toBe(true);
+ });
+
+ it('should expose context in before and afterSave and let keys be set individually', async () => {
+ let calledBefore = false;
+ let calledAfter = false;
+ Parse.Cloud.beforeSave('MyClass', req => {
+ req.context.some = 'value';
+ req.context.yolo = 1;
+ calledBefore = true;
+ });
+ Parse.Cloud.afterSave('MyClass', req => {
+ expect(req.context.yolo).toBe(1);
+ expect(req.context.some).toBe('value');
+ calledAfter = true;
+ });
+
+ const object = new Parse.Object('MyClass');
+ await object.save();
+ expect(calledBefore).toBe(true);
+ expect(calledAfter).toBe(true);
+ });
+});
+
+describe('beforeLogin hook', () => {
+ it('should run beforeLogin with correct credentials', async done => {
+ let hit = 0;
+ Parse.Cloud.beforeLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('tupac');
+ });
+
+ await Parse.User.signUp('tupac', 'shakur');
+ const user = await Parse.User.logIn('tupac', 'shakur');
+ expect(hit).toBe(1);
+ expect(user).toBeDefined();
+ expect(user.getUsername()).toBe('tupac');
+ expect(user.getSessionToken()).toBeDefined();
+ done();
+ });
+
+ it('should be able to block login if an error is thrown', async done => {
+ let hit = 0;
+ Parse.Cloud.beforeLogin(req => {
+ hit++;
+ if (req.object.get('isBanned')) {
+ throw new Error('banned account');
+ }
+ });
+
+ const user = await Parse.User.signUp('tupac', 'shakur');
+ await user.save({ isBanned: true });
+
+ try {
+ await Parse.User.logIn('tupac', 'shakur');
+ throw new Error('should not have been logged in.');
+ } catch (e) {
+ expect(e.message).toBe('banned account');
+ }
+ expect(hit).toBe(1);
+ done();
+ });
+
+ it('should be able to block login if an error is thrown even if the user has a attached file', async done => {
+ let hit = 0;
+ Parse.Cloud.beforeLogin(req => {
+ hit++;
+ if (req.object.get('isBanned')) {
+ throw new Error('banned account');
+ }
+ });
+
+ const user = await Parse.User.signUp('tupac', 'shakur');
+ const base64 = 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=';
+ const file = new Parse.File('myfile.txt', { base64 });
+ await file.save();
+ await user.save({ isBanned: true, file });
+
+ try {
+ await Parse.User.logIn('tupac', 'shakur');
+ throw new Error('should not have been logged in.');
+ } catch (e) {
+ expect(e.message).toBe('banned account');
+ }
+ expect(hit).toBe(1);
+ done();
+ });
+
+ it('should not run beforeLogin with incorrect credentials', async done => {
+ let hit = 0;
+ Parse.Cloud.beforeLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('tupac');
+ });
+
+ await Parse.User.signUp('tupac', 'shakur');
+ try {
+ await Parse.User.logIn('tony', 'shakur');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ expect(hit).toBe(0);
+ done();
+ });
+
+ it('should not run beforeLogin on sign up', async done => {
+ let hit = 0;
+ Parse.Cloud.beforeLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('tupac');
+ });
+
+ const user = await Parse.User.signUp('tupac', 'shakur');
+ expect(user).toBeDefined();
+ expect(hit).toBe(0);
+ done();
+ });
+
+ it('should trigger afterLogout hook on logout', async done => {
+ let userId;
+ Parse.Cloud.afterLogout(req => {
+ expect(req.object.className).toEqual('_Session');
+ expect(req.object.id).toBeDefined();
+ const user = req.object.get('user');
+ expect(user).toBeDefined();
+ userId = user.id;
+ });
+
+ const user = await Parse.User.signUp('user', 'pass');
+ await Parse.User.logOut();
+ expect(user.id).toBe(userId);
+ done();
+ });
+
+ it('does not crash server when throwing in afterLogin hook', async () => {
+ const error = new Parse.Error(2000, 'afterLogin error');
+ const trigger = {
+ afterLogin() {
+ throw error;
+ },
+ };
+ const spy = spyOn(trigger, 'afterLogin').and.callThrough();
+ Parse.Cloud.afterLogin(trigger.afterLogin);
+ await Parse.User.signUp('user', 'pass');
+ const response = await Parse.User.logIn('user', 'pass').catch(e => e);
+ expect(spy).toHaveBeenCalled();
+ expect(response).toEqual(error);
+ });
+
+ it('does not crash server when throwing in afterLogout hook', async () => {
+ const error = new Parse.Error(2000, 'afterLogout error');
+ const trigger = {
+ afterLogout() {
+ throw error;
+ },
+ };
+ const spy = spyOn(trigger, 'afterLogout').and.callThrough();
+ Parse.Cloud.afterLogout(trigger.afterLogout);
+ await Parse.User.signUp('user', 'pass');
+ const response = await Parse.User.logOut().catch(e => e);
+ expect(spy).toHaveBeenCalled();
+ expect(response).toEqual(error);
+ });
+
+ it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)('should have expected data in request in beforeLogin', async done => {
+ Parse.Cloud.beforeLogin(req => {
+ expect(req.object).toBeDefined();
+ expect(req.user).toBeUndefined();
+ expect(req.headers).toBeDefined();
+ expect(req.ip).toBeDefined();
+ expect(req.installationId).toBeDefined();
+ expect(req.context).toBeDefined();
+ expect(req.config).toBeDefined();
+ });
+
+ await Parse.User.signUp('tupac', 'shakur');
+ await Parse.User.logIn('tupac', 'shakur');
+ done();
+ });
+
+ it('afterFind should not be triggered when saving an object', async () => {
+ let beforeSaves = 0;
+ Parse.Cloud.beforeSave('SavingTest', () => {
+ beforeSaves++;
+ });
+
+ let afterSaves = 0;
+ Parse.Cloud.afterSave('SavingTest', () => {
+ afterSaves++;
+ });
+
+ let beforeFinds = 0;
+ Parse.Cloud.beforeFind('SavingTest', () => {
+ beforeFinds++;
+ });
+
+ let afterFinds = 0;
+ Parse.Cloud.afterFind('SavingTest', () => {
+ afterFinds++;
+ });
+
+ const obj = new Parse.Object('SavingTest');
+ obj.set('someField', 'some value 1');
+ await obj.save();
+
+ expect(beforeSaves).toEqual(1);
+ expect(afterSaves).toEqual(1);
+ expect(beforeFinds).toEqual(0);
+ expect(afterFinds).toEqual(0);
+
+ obj.set('someField', 'some value 2');
+ await obj.save();
+
+ expect(beforeSaves).toEqual(2);
+ expect(afterSaves).toEqual(2);
+ expect(beforeFinds).toEqual(0);
+ expect(afterFinds).toEqual(0);
+
+ await obj.fetch();
+
+ expect(beforeSaves).toEqual(2);
+ expect(afterSaves).toEqual(2);
+ expect(beforeFinds).toEqual(1);
+ expect(afterFinds).toEqual(1);
+
+ obj.set('someField', 'some value 3');
+ await obj.save();
+
+ expect(beforeSaves).toEqual(3);
+ expect(afterSaves).toEqual(3);
+ expect(beforeFinds).toEqual(1);
+ expect(afterFinds).toEqual(1);
+ });
+});
+
+describe('afterLogin hook', () => {
+ it('should run afterLogin after successful login', async done => {
+ let hit = 0;
+ Parse.Cloud.afterLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('testuser');
+ });
+
+ await Parse.User.signUp('testuser', 'p@ssword');
+ const user = await Parse.User.logIn('testuser', 'p@ssword');
+ expect(hit).toBe(1);
+ expect(user).toBeDefined();
+ expect(user.getUsername()).toBe('testuser');
+ expect(user.getSessionToken()).toBeDefined();
+ done();
+ });
+
+ it('should not run afterLogin after unsuccessful login', async done => {
+ let hit = 0;
+ Parse.Cloud.afterLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('testuser');
+ });
+
+ await Parse.User.signUp('testuser', 'p@ssword');
+ try {
+ await Parse.User.logIn('testuser', 'badpassword');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
+ }
+ expect(hit).toBe(0);
+ done();
+ });
+
+ it('should not run afterLogin on sign up', async done => {
+ let hit = 0;
+ Parse.Cloud.afterLogin(req => {
+ hit++;
+ expect(req.object.get('username')).toEqual('testuser');
+ });
+
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ expect(user).toBeDefined();
+ expect(hit).toBe(0);
+ done();
+ });
+
+ it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)('should have expected data in request in afterLogin', async done => {
+ Parse.Cloud.afterLogin(req => {
+ expect(req.object).toBeDefined();
+ expect(req.user).toBeDefined();
+ expect(req.headers).toBeDefined();
+ expect(req.ip).toBeDefined();
+ expect(req.installationId).toBeDefined();
+ expect(req.context).toBeDefined();
+ expect(req.config).toBeDefined();
+ });
+
+ await Parse.User.signUp('testuser', 'p@ssword');
+ await Parse.User.logIn('testuser', 'p@ssword');
+ done();
+ });
+
+ it('context options should override _context object property when saving a new object', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ expect(req.context.hello).not.toBeDefined();
+ expect(req._context).not.toBeDefined();
+ expect(req.object._context).not.toBeDefined();
+ expect(req.object.context).not.toBeDefined();
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ expect(req.context.hello).not.toBeDefined();
+ expect(req._context).not.toBeDefined();
+ expect(req.object._context).not.toBeDefined();
+ expect(req.object.context).not.toBeDefined();
+ });
+ await request({
+ url: 'http://localhost:8378/1/classes/TestObject',
+ method: 'POST',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Cloud-Context': '{"a":"a"}',
+ },
+ body: JSON.stringify({ _context: { hello: 'world' } }),
+ });
+
+ });
+
+ it('should have access to context when saving a new object', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj = new TestObject();
+ await obj.save(null, { context: { a: 'a' } });
+ });
+
+ it('should have access to context when saving an existing object', async () => {
+ const obj = new TestObject();
+ await obj.save(null);
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ await obj.save(null, { context: { a: 'a' } });
+ });
+
+ it('should have access to context when saving a new object in a trigger', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TriggerObject', async () => {
+ const obj = new TestObject();
+ await obj.save(null, { context: { a: 'a' } });
+ });
+ const obj = new Parse.Object('TriggerObject');
+ await obj.save(null);
+ });
+
+ it('should have access to context when cascade-saving objects', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.beforeSave('TestObject2', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject2', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject2');
+ obj.set('obj2', obj2);
+ await obj.save(null, { context: { a: 'a' } });
+ });
+
+ it('should have access to context as saveAll argument', async () => {
+ Parse.Cloud.beforeSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterSave('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj1 = new TestObject();
+ const obj2 = new TestObject();
+ await Parse.Object.saveAll([obj1, obj2], { context: { a: 'a' } });
+ });
+
+ it('should have access to context as destroyAll argument', async () => {
+ Parse.Cloud.beforeDelete('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterDelete('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj1 = new TestObject();
+ const obj2 = new TestObject();
+ await Parse.Object.saveAll([obj1, obj2]);
+ await Parse.Object.destroyAll([obj1, obj2], { context: { a: 'a' } });
+ });
+
+ it('should have access to context as destroy a object', async () => {
+ Parse.Cloud.beforeDelete('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ Parse.Cloud.afterDelete('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj = new TestObject();
+ await obj.save();
+ await obj.destroy({ context: { a: 'a' } });
+ });
+
+ it('should have access to context in beforeFind hook', async () => {
+ Parse.Cloud.beforeFind('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const query = new Parse.Query('TestObject');
+ return query.find({ context: { a: 'a' } });
+ });
+
+ it('should have access to context when cloud function is called.', async () => {
+ Parse.Cloud.define('contextTest', async req => {
+ expect(req.context.a).toEqual('a');
+ return {};
+ });
+
+ await Parse.Cloud.run('contextTest', {}, { context: { a: 'a' } });
+ });
+
+ it('afterFind should have access to context', async () => {
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ });
+ const obj = new TestObject();
+ await obj.save();
+ const query = new Parse.Query(TestObject);
+ await query.find({ context: { a: 'a' } });
+ });
+
+ it('beforeFind and afterFind should have access to context while making fetch call', async () => {
+ Parse.Cloud.beforeFind('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ expect(req.context.b).toBeUndefined();
+ req.context.b = 'b';
+ });
+ Parse.Cloud.afterFind('TestObject', req => {
+ expect(req.context.a).toEqual('a');
+ expect(req.context.b).toEqual('b');
+ });
+ const obj = new TestObject();
+ await obj.save();
+ await obj.fetch({ context: { a: 'a' } });
+ });
+});
+
+describe('saveFile hooks', () => {
+ it('beforeSave(Parse.File) should return file that is already saved and not save anything to files adapter', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ const newFile = new Parse.File('some-file.txt');
+ newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt';
+ return newFile;
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ expect(result._name).toBe('some-file.txt');
+ expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt');
+ expect(createFileSpy).not.toHaveBeenCalled();
+ });
+
+ it('beforeSave(Parse.File) should throw error', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ throw new Parse.Error(400, 'some-error-message');
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ try {
+ await file.save({ useMasterKey: true });
+ } catch (error) {
+ expect(error.message).toBe('some-error-message');
+ }
+ });
+
+ it('beforeSaveFile should have config', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeSave(Parse.File, req => {
+ expect(req.config).toBeDefined();
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ });
+
+ it('beforeSave(Parse.File) should change values of uploaded file by editing fileObject directly', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.File, async req => {
+ expect(req.triggerName).toEqual('beforeSave');
+ expect(req.master).toBe(true);
+ req.file.addMetadata('foo', 'bar');
+ req.file.addTag('tagA', 'some-tag');
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ const newData = new Buffer([1, 2, 3]);
+ const newOptions = {
+ tags: {
+ tagA: 'some-tag',
+ },
+ metadata: {
+ foo: 'bar',
+ },
+ };
+ expect(createFileSpy).toHaveBeenCalledWith(
+ jasmine.any(String),
+ newData,
+ 'text/plain',
+ newOptions
+ );
+ });
+
+ it('beforeSave(Parse.File) should change values by returning new fileObject', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.File, async req => {
+ expect(req.triggerName).toEqual('beforeSave');
+ expect(req.fileSize).toBe(3);
+ const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6], 'application/pdf');
+ newFile.setMetadata({ foo: 'bar' });
+ newFile.setTags({ tagA: 'some-tag' });
+ return newFile;
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBeInstanceOf(Parse.File);
+ const newData = new Buffer([4, 5, 6]);
+ const newContentType = 'application/pdf';
+ const newOptions = {
+ tags: {
+ tagA: 'some-tag',
+ },
+ metadata: {
+ foo: 'bar',
+ },
+ };
+ expect(createFileSpy).toHaveBeenCalledWith(
+ jasmine.any(String),
+ newData,
+ newContentType,
+ newOptions
+ );
+ const expectedFileName = 'donald_duck.pdf';
+ expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length);
+ });
+
+ it('beforeSave(Parse.File) should contain metadata and tags saved from client', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.File, async req => {
+ expect(req.triggerName).toEqual('beforeSave');
+ expect(req.fileSize).toBe(3);
+ expect(req.file).toBeInstanceOf(Parse.File);
+ expect(req.file.name()).toBe('popeye.txt');
+ expect(req.file.metadata()).toEqual({ foo: 'bar' });
+ expect(req.file.tags()).toEqual({ bar: 'foo' });
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ file.setMetadata({ foo: 'bar' });
+ file.setTags({ bar: 'foo' });
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBeInstanceOf(Parse.File);
+ const options = {
+ metadata: { foo: 'bar' },
+ tags: { bar: 'foo' },
+ };
+ expect(createFileSpy).toHaveBeenCalledWith(
+ jasmine.any(String),
+ jasmine.any(Buffer),
+ 'text/plain',
+ options
+ );
+ });
+
+ it('beforeSave(Parse.File) should return same file data with new file name', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const config = Config.get('test');
+ config.filesController.options.preserveFileName = true;
+ Parse.Cloud.beforeSave(Parse.File, async ({ file }) => {
+ expect(file.name()).toBe('popeye.txt');
+ const fileData = await file.getData();
+ const newFile = new Parse.File('2020-04-01.txt', { base64: fileData });
+ return newFile;
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result.name()).toBe('2020-04-01.txt');
+ });
+
+ it('afterSave(Parse.File) should set fileSize to null if beforeSave returns an already saved file', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.File, req => {
+ expect(req.fileSize).toBe(3);
+ const newFile = new Parse.File('some-file.txt');
+ newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt';
+ return newFile;
+ });
+ Parse.Cloud.afterSave(Parse.File, req => {
+ expect(req.fileSize).toBe(null);
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(result);
+ expect(result._name).toBe('some-file.txt');
+ expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt');
+ expect(createFileSpy).not.toHaveBeenCalled();
});
- it('beforeSave change propagates through the save response', (done) => {
- Parse.Cloud.beforeSave('ChangingObject', function(request, response) {
- request.object.set('foo', 'baz');
- response.success();
+ it('afterSave(Parse.File) should throw error', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.afterSave(Parse.File, async () => {
+ throw new Parse.Error(400, 'some-error-message');
});
- let obj = new Parse.Object('ChangingObject');
- obj.save({ foo: 'bar' }).then((objAgain) => {
- expect(objAgain.get('foo')).toEqual('baz');
+ const filename = 'donald_duck.pdf';
+ const file = new Parse.File(filename, [1, 2, 3], 'text/plain');
+ try {
+ await file.save({ useMasterKey: true });
+ } catch (error) {
+ expect(error.message).toBe('some-error-message');
+ }
+ });
+
+ it('afterSave(Parse.File) should call with fileObject', async done => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeSave(Parse.File, async req => {
+ req.file.setTags({ tagA: 'some-tag' });
+ req.file.setMetadata({ foo: 'bar' });
+ });
+ Parse.Cloud.afterSave(Parse.File, async req => {
+ expect(req.master).toBe(true);
+ expect(req.file._tags).toEqual({ tagA: 'some-tag' });
+ expect(req.file._metadata).toEqual({ foo: 'bar' });
done();
- }, (e) => {
- fail('Should not have failed to save.');
+ });
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ });
+
+ it('afterSave(Parse.File) should change fileSize when file data changes', async done => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeSave(Parse.File, async req => {
+ expect(req.fileSize).toBe(3);
+ expect(req.master).toBe(true);
+ const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6, 7, 8, 9], 'application/pdf');
+ return newFile;
+ });
+ Parse.Cloud.afterSave(Parse.File, async req => {
+ expect(req.fileSize).toBe(6);
+ expect(req.master).toBe(true);
done();
});
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
});
- it('test cloud function parameter validation success', (done) => {
- // Register a function with validation
- Parse.Cloud.define('functionWithParameterValidation', (req, res) => {
- res.success('works');
- }, (request) => {
- return request.params.success === 100;
+ it('beforeDelete(Parse.File) should call with fileObject', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeDelete(Parse.File, req => {
+ expect(req.file).toBeInstanceOf(Parse.File);
+ expect(req.file._name).toEqual('popeye.txt');
+ expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
+ expect(req.fileSize).toBe(null);
});
+ const file = new Parse.File('popeye.txt');
+ await file.destroy({ useMasterKey: true });
+ });
- Parse.Cloud.run('functionWithParameterValidation', {"success":100}).then((s) => {
+ it('beforeDelete(Parse.File) should throw error', async done => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeDelete(Parse.File, () => {
+ throw new Error('some error message');
+ });
+ const file = new Parse.File('popeye.txt');
+ try {
+ await file.destroy({ useMasterKey: true });
+ } catch (error) {
+ expect(error.message).toBe('some error message');
done();
- }, (e) => {
- fail('Validation should not have failed.');
+ }
+ });
+
+ it('afterDelete(Parse.File) should call with fileObject', async done => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeDelete(Parse.File, req => {
+ expect(req.file).toBeInstanceOf(Parse.File);
+ expect(req.file._name).toEqual('popeye.txt');
+ expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
+ });
+ Parse.Cloud.afterDelete(Parse.File, req => {
+ expect(req.file).toBeInstanceOf(Parse.File);
+ expect(req.file._name).toEqual('popeye.txt');
+ expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt');
done();
});
+ const file = new Parse.File('popeye.txt');
+ await file.destroy({ useMasterKey: true });
});
- it('doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)', done => {
- Parse.Cloud.define('testQuery', function(request, response) {
- response.success(request.user.get('data'));
+ it('beforeSave(Parse.File) should not change file if nothing is returned', async () => {
+ await reconfigureServer({ filesAdapter: mockAdapter });
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ return;
});
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ const result = await file.save({ useMasterKey: true });
+ expect(result).toBe(file);
+ });
- Parse.User.signUp('user', 'pass')
- .then(user => {
- user.set('data', 'AAA');
- return user.save();
- })
- .then(() => Parse.Cloud.run('testQuery'))
- .then(result => {
- expect(result).toEqual('AAA');
- Parse.User.current().set('data', 'BBB');
- return Parse.User.current().save(null, {useMasterKey: true});
- })
- .then(() => Parse.Cloud.run('testQuery'))
- .then(result => {
- expect(result).toEqual('BBB');
+ it('throw custom error from beforeSave(Parse.File) ', async done => {
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
+ });
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
done();
+ }
+ });
+
+ it('throw empty error from beforeSave(Parse.File)', async done => {
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ throw null;
});
+ try {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(130);
+ done();
+ }
});
+});
- it('clears out the user cache for all sessions when the user is changed', done => {
- let session1;
- let session2;
- let user;
- const cacheAdapter = new InMemoryCacheAdapter({ ttl: 100000000 });
- reconfigureServer({ cacheAdapter })
- .then(() => {
- Parse.Cloud.define('checkStaleUser', (request, response) => {
- response.success(request.user.get('data'));
- });
-
- user = new Parse.User();
- user.set('username', 'test');
- user.set('password', 'moon-y');
- user.set('data', 'first data');
- return user.signUp();
- })
- .then(user => {
- session1 = user.getSessionToken();
- return rp({
- uri: 'http://localhost:8378/1/login?username=test&password=moon-y',
- json: true,
+describe('Parse.File hooks', () => {
+ it('find hooks should run', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ const user = await Parse.User.signUp('username', 'password');
+ const hooks = {
+ beforeFind(req) {
+ expect(req).toBeDefined();
+ expect(req.file).toBeDefined();
+ expect(req.triggerName).toBe('beforeFind');
+ expect(req.master).toBeFalse();
+ expect(req.log).toBeDefined();
+ },
+ afterFind(req) {
+ expect(req).toBeDefined();
+ expect(req.file).toBeDefined();
+ expect(req.triggerName).toBe('afterFind');
+ expect(req.master).toBeFalse();
+ expect(req.log).toBeDefined();
+ expect(req.forceDownload).toBeFalse();
+ },
+ };
+ for (const hook in hooks) {
+ spyOn(hooks, hook).and.callThrough();
+ Parse.Cloud[hook](Parse.File, hooks[hook]);
+ }
+ await request({
+ url: file.url(),
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ });
+ for (const hook in hooks) {
+ expect(hooks[hook]).toHaveBeenCalled();
+ }
+ });
+
+ it('beforeFind can throw', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ const user = await Parse.User.signUp('username', 'password');
+ const hooks = {
+ beforeFind() {
+ throw 'unauthorized';
+ },
+ afterFind() { },
+ };
+ for (const hook in hooks) {
+ spyOn(hooks, hook).and.callThrough();
+ Parse.Cloud[hook](Parse.File, hooks[hook]);
+ }
+ await expectAsync(
+ request({
+ url: file.url(),
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': user.getSessionToken(),
},
+ }).catch(e => {
+ throw new Parse.Error(e.data.code, e.data.error);
})
- })
- .then(body => {
- session2 = body.sessionToken;
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized'));
- //Ensure both session tokens are in the cache
- return Parse.Cloud.run('checkStaleUser')
- })
- .then(() => rp({
- method: 'POST',
- uri: 'http://localhost:8378/1/functions/checkStaleUser',
- json: true,
+ expect(hooks.beforeFind).toHaveBeenCalled();
+ expect(hooks.afterFind).not.toHaveBeenCalled();
+ });
+
+ it('afterFind can throw', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ const user = await Parse.User.signUp('username', 'password');
+ const hooks = {
+ beforeFind() { },
+ afterFind() {
+ throw 'unauthorized';
+ },
+ };
+ for (const hook in hooks) {
+ spyOn(hooks, hook).and.callThrough();
+ Parse.Cloud[hook](Parse.File, hooks[hook]);
+ }
+ await expectAsync(
+ request({
+ url: file.url(),
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ }).catch(e => {
+ throw new Parse.Error(e.data.code, e.data.error);
+ })
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized'));
+ for (const hook in hooks) {
+ expect(hooks[hook]).toHaveBeenCalled();
+ }
+ });
+
+ it('can force download', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ const user = await Parse.User.signUp('username', 'password');
+ Parse.Cloud.afterFind(Parse.File, req => {
+ req.forceDownload = true;
+ });
+ const response = await request({
+ url: file.url(),
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
- 'X-Parse-Session-Token': session2,
- }
- }))
- .then(() => Parse.Promise.all([cacheAdapter.get('test:user:' + session1), cacheAdapter.get('test:user:' + session2)]))
- .then(cachedVals => {
- expect(cachedVals[0].objectId).toEqual(user.id);
- expect(cachedVals[1].objectId).toEqual(user.id);
-
- //Change with session 1 and then read with session 2.
- user.set('data', 'second data');
- return user.save()
- })
- .then(() => rp({
- method: 'POST',
- uri: 'http://localhost:8378/1/functions/checkStaleUser',
- json: true,
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ });
+ expect(response.headers['content-disposition']).toBe(`attachment;filename=${file._name}`);
+ });
+
+ it('can set custom response headers in afterFind', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ Parse.Cloud.afterFind(Parse.File, req => {
+ req.responseHeaders['X-Custom-Header'] = 'custom-value';
+ });
+ const response = await request({
+ url: file.url(),
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ });
+ expect(response.headers['x-custom-header']).toBe('custom-value');
+ expect(response.headers['x-content-type-options']).toBe('nosniff');
+ });
+
+ it('can override default response headers in afterFind', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ Parse.Cloud.afterFind(Parse.File, req => {
+ delete req.responseHeaders['X-Content-Type-Options'];
+ });
+ const response = await request({
+ url: file.url(),
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
- 'X-Parse-Session-Token': session2,
+ },
+ });
+ expect(response.headers['x-content-type-options']).toBeUndefined();
+ });
+
+ it('beforeFind blocks metadata endpoint', async () => {
+ const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
+ await file.save({ useMasterKey: true });
+ Parse.Cloud.beforeFind(Parse.File, () => {
+ throw 'unauthorized';
+ });
+ await expectAsync(
+ request({
+ url: `http://localhost:8378/1/files/test/metadata/${file._name}`,
+ }).catch(e => {
+ throw new Parse.Error(e.data.code, e.data.error);
+ })
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized'));
+ });
+});
+
+describe('Cloud Config hooks', () => {
+ function testConfig() {
+ return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
+ }
+
+ it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)('beforeSave(Parse.Config) can run hook with new config', async () => {
+ let count = 0;
+ Parse.Cloud.beforeSave(Parse.Config, (req) => {
+ expect(req.object).toBeDefined();
+ expect(req.original).toBeUndefined();
+ expect(req.user).toBeUndefined();
+ expect(req.headers).toBeDefined();
+ expect(req.ip).toBeDefined();
+ expect(req.installationId).toBeDefined();
+ expect(req.context).toBeDefined();
+ const config = req.object;
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ count += 1;
+ });
+ await testConfig();
+ const config = await Parse.Config.get({ useMasterKey: true });
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ expect(count).toBe(1);
+ });
+
+ it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)('beforeSave(Parse.Config) can run hook with existing config', async () => {
+ let count = 0;
+ Parse.Cloud.beforeSave(Parse.Config, (req) => {
+ if (count === 0) {
+ expect(req.object.get('number')).toBe(12);
+ expect(req.original).toBeUndefined();
}
- }))
- .then(body => {
- expect(body.result).toEqual('second data');
- done();
- })
- .catch(error => {
- fail(JSON.stringify(error));
- done();
+ if (count === 1) {
+ expect(req.object.get('number')).toBe(13);
+ expect(req.original.get('number')).toBe(12);
+ }
+ count += 1;
});
+ await testConfig();
+ await Parse.Config.save({ number: 13 });
+ expect(count).toBe(2);
});
- it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => {
- Parse.Cloud.beforeSave('BeforeSaveUnchanged', (req, res) => {
- res.success();
- });
-
- var TestObject = Parse.Object.extend("TestObject");
- var NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave");
- var BeforeSaveObject = Parse.Object.extend("BeforeSaveUnchanged");
-
- var aTestObject = new TestObject();
- aTestObject.set("foo", "bar");
- aTestObject.save()
- .then(aTestObject => {
- var aNoBeforeSaveObj = new NoBeforeSaveObject();
- aNoBeforeSaveObj.set("aTestObject", aTestObject);
- expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar");
- return aNoBeforeSaveObj.save();
- })
- .then(aNoBeforeSaveObj => {
- expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar");
-
- var aBeforeSaveObj = new BeforeSaveObject();
- aBeforeSaveObj.set("aTestObject", aTestObject);
- expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar");
- return aBeforeSaveObj.save();
- })
- .then(aBeforeSaveObj => {
- expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar");
- done();
+ it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)('beforeSave(Parse.Config) should not change config if nothing is returned', async () => {
+ let count = 0;
+ Parse.Cloud.beforeSave(Parse.Config, () => {
+ count += 1;
+ return;
});
+ await testConfig();
+ const config = await Parse.Config.get({ useMasterKey: true });
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ expect(count).toBe(1);
});
- it_exclude_dbs(['postgres'])('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => {
- var TestObject = Parse.Object.extend('TestObject');
- var NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');
- var BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
+ it('beforeSave(Parse.Config) throw custom error', async () => {
+ Parse.Cloud.beforeSave(Parse.Config, () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
+ });
+ try {
+ await testConfig();
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBe('It should fail');
+ }
+ });
- Parse.Cloud.beforeSave('BeforeSaveChanged', (req, res) => {
- var object = req.object;
- object.set('before', 'save');
- res.success();
+ it('beforeSave(Parse.Config) throw string error', async () => {
+ Parse.Cloud.beforeSave(Parse.Config, () => {
+ throw 'before save failed';
});
+ try {
+ await testConfig();
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBe('before save failed');
+ }
+ });
- Parse.Cloud.define('removeme', (req, res) => {
- var testObject = new TestObject();
- testObject.save()
- .then(testObject => {
- var object = new NoBeforeSaveObject({remove: testObject});
- return object.save();
- })
- .then(object => {
- object.unset('remove');
- return object.save();
- })
- .then(object => {
- res.success(object);
- });
+ it('beforeSave(Parse.Config) throw empty error', async () => {
+ Parse.Cloud.beforeSave(Parse.Config, () => {
+ throw null;
});
+ try {
+ await testConfig();
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(e.message).toBe('Script failed. Unknown error.');
+ }
+ });
- Parse.Cloud.define('removeme2', (req, res) => {
- var testObject = new TestObject();
- testObject.save()
- .then(testObject => {
- var object = new BeforeSaveObject({remove: testObject});
- return object.save();
- })
- .then(object => {
- object.unset('remove');
- return object.save();
- })
- .then(object => {
- res.success(object);
- });
+ it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)('afterSave(Parse.Config) can run hook with new config', async () => {
+ let count = 0;
+ Parse.Cloud.afterSave(Parse.Config, (req) => {
+ expect(req.object).toBeDefined();
+ expect(req.original).toBeUndefined();
+ expect(req.user).toBeUndefined();
+ expect(req.headers).toBeDefined();
+ expect(req.ip).toBeDefined();
+ expect(req.installationId).toBeDefined();
+ expect(req.context).toBeDefined();
+ const config = req.object;
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ count += 1;
});
+ await testConfig();
+ const config = await Parse.Config.get({ useMasterKey: true });
+ expect(config.get('internal')).toBe('i');
+ expect(config.get('string')).toBe('s');
+ expect(config.get('number')).toBe(12);
+ expect(count).toBe(1);
+ });
- Parse.Cloud.run('removeme')
- .then(aNoBeforeSaveObj => {
- expect(aNoBeforeSaveObj.get('remove')).toEqual(undefined);
-
- return Parse.Cloud.run('removeme2');
- })
- .then(aBeforeSaveObj => {
- expect(aBeforeSaveObj.get('before')).toEqual('save');
- expect(aBeforeSaveObj.get('remove')).toEqual(undefined);
- done();
+ it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)('afterSave(Parse.Config) can run hook with existing config', async () => {
+ let count = 0;
+ Parse.Cloud.afterSave(Parse.Config, (req) => {
+ if (count === 0) {
+ expect(req.object.get('number')).toBe(12);
+ expect(req.original).toBeUndefined();
+ }
+ if (count === 1) {
+ expect(req.object.get('number')).toBe(13);
+ expect(req.original.get('number')).toBe(12);
+ }
+ count += 1;
});
+ await testConfig();
+ await Parse.Config.save({ number: 13 });
+ expect(count).toBe(2);
});
- it_exclude_dbs(['postgres'])('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => {
- var TestObject = Parse.Object.extend('TestObject');
- var BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
+ it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)('afterSave(Parse.Config) should throw error', async () => {
+ Parse.Cloud.afterSave(Parse.Config, () => {
+ throw new Parse.Error(400, 'It should fail');
+ });
+ try {
+ await testConfig();
+ fail('error should have thrown');
+ } catch (e) {
+ expect(e.code).toBe(400);
+ expect(e.message).toBe('It should fail');
+ }
+ });
+});
- Parse.Cloud.beforeSave('BeforeSaveChanged', (req, res) => {
- var object = req.object;
- object.set('before', 'save');
- object.unset('remove');
- res.success();
+describe('sendEmail', () => {
+ it('can send email via Parse.Cloud', async done => {
+ const emailAdapter = {
+ sendMail: mailData => {
+ expect(mailData).toBeDefined();
+ expect(mailData.to).toBe('test');
+ reconfigureServer().then(done, done);
+ },
+ };
+ await reconfigureServer({
+ emailAdapter: emailAdapter,
});
+ const mailData = { to: 'test' };
+ await Parse.Cloud.sendEmail(mailData);
+ });
- let object;
- let testObject = new TestObject({key: 'value'});
- testObject.save().then(() => {
- object = new BeforeSaveObject();
- return object.save().then(() =>Â {
- object.set({remove:testObject})
- return object.save();
- });
- }).then((objectAgain) => {
- expect(objectAgain.get('remove')).toBeUndefined();
- expect(object.get('remove')).toBeUndefined();
- done();
- }).fail((err) => {
- console.error(err);
- done();
- })
+ it('cannot send email without adapter', async () => {
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => { });
+ await Parse.Cloud.sendEmail({});
+ expect(logger.error).toHaveBeenCalledWith(
+ 'Failed to send email because no mail adapter is configured for Parse Server.'
+ );
});
+});
- it_exclude_dbs(['postgres'])('should not include relation op (regression test for #1606)', done => {
- var TestObject = Parse.Object.extend('TestObject');
- var BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged');
- let testObj;
- Parse.Cloud.beforeSave('BeforeSaveChanged', (req, res) => {
- var object = req.object;
- object.set('before', 'save');
- testObj = new TestObject();
- testObj.save().then(() =>Â {
- object.relation('testsRelation').add(testObj);
- res.success();
- })
+describe('beforePasswordResetRequest hook', () => {
+ it('should run beforePasswordResetRequest with valid user', async () => {
+ let hit = 0;
+ let sendPasswordResetEmailCalled = false;
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => {
+ sendPasswordResetEmailCalled = true;
+ },
+ sendMail: () => {},
+ };
+
+ await reconfigureServer({
+ appName: 'test',
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
});
- let object = new BeforeSaveObject();
- object.save().then((objectAgain) => {
- // Originally it would throw as it would be a non-relation
- expect(() => { objectAgain.relation('testsRelation') }).not.toThrow();
- done();
- }).fail((err) => {
- console.error(err);
- done();
- })
+ Parse.Cloud.beforePasswordResetRequest(req => {
+ hit++;
+ expect(req.object).toBeDefined();
+ expect(req.object.get('email')).toEqual('test@example.com');
+ expect(req.object.get('username')).toEqual('testuser');
+ });
+
+ const user = new Parse.User();
+ user.setUsername('testuser');
+ user.setPassword('password');
+ user.set('email', 'test@example.com');
+ await user.signUp();
+
+ await Parse.User.requestPasswordReset('test@example.com');
+ expect(hit).toBe(1);
+ expect(sendPasswordResetEmailCalled).toBe(true);
+ });
+
+ it('should be able to block password reset request if an error is thrown', async () => {
+ let hit = 0;
+ let sendPasswordResetEmailCalled = false;
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => {
+ sendPasswordResetEmailCalled = true;
+ },
+ sendMail: () => {},
+ };
+
+ await reconfigureServer({
+ appName: 'test',
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ Parse.Cloud.beforePasswordResetRequest(req => {
+ hit++;
+ throw new Error('password reset blocked');
+ });
+
+ const user = new Parse.User();
+ user.setUsername('testuser');
+ user.setPassword('password');
+ user.set('email', 'test@example.com');
+ await user.signUp();
+
+ try {
+ await Parse.User.requestPasswordReset('test@example.com');
+ throw new Error('should not have sent password reset email.');
+ } catch (e) {
+ expect(e.message).toBe('password reset blocked');
+ }
+ expect(hit).toBe(1);
+ expect(sendPasswordResetEmailCalled).toBe(false);
+ });
+
+ it('should not run beforePasswordResetRequest if email does not exist', async () => {
+ let hit = 0;
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => {},
+ sendMail: () => {},
+ };
+
+ await reconfigureServer({
+ appName: 'test',
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ Parse.Cloud.beforePasswordResetRequest(req => {
+ hit++;
+ });
+
+ await Parse.User.requestPasswordReset('nonexistent@example.com');
+
+ expect(hit).toBe(0);
+ });
+
+ it('should have expected data in request in beforePasswordResetRequest', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => {},
+ sendMail: () => {},
+ };
+
+ await reconfigureServer({
+ appName: 'test',
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ const base64 = 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=';
+ const file = new Parse.File('myfile.txt', { base64 });
+ await file.save();
+
+ Parse.Cloud.beforePasswordResetRequest(req => {
+ expect(req.object).toBeDefined();
+ expect(req.object.get('email')).toBeDefined();
+ expect(req.object.get('email')).toBe('test2@example.com');
+ expect(req.object.get('file')).toBeDefined();
+ expect(req.object.get('file')).toBeInstanceOf(Parse.File);
+ expect(req.object.get('file').name()).toContain('myfile.txt');
+ expect(req.headers).toBeDefined();
+ expect(req.ip).toBeDefined();
+ expect(req.installationId).toBeDefined();
+ expect(req.context).toBeDefined();
+ expect(req.config).toBeDefined();
+ });
+
+ const user = new Parse.User();
+ user.setUsername('testuser2');
+ user.setPassword('password');
+ user.set('email', 'test2@example.com');
+ user.set('file', file);
+ await user.signUp();
+
+ await Parse.User.requestPasswordReset('test2@example.com');
+ });
+
+ it('should validate that only _User class is allowed for beforePasswordResetRequest', () => {
+ expect(() => {
+ Parse.Cloud.beforePasswordResetRequest('SomeClass', () => { });
+ }).toThrow('Only the _User class is allowed for the beforeLogin, afterLogin, and beforePasswordResetRequest triggers');
+ expect(() => {
+ Parse.Cloud.beforePasswordResetRequest(() => { });
+ }).not.toThrow();
+ expect(() => {
+ Parse.Cloud.beforePasswordResetRequest('_User', () => { });
+ }).not.toThrow();
+ expect(() => {
+ Parse.Cloud.beforePasswordResetRequest(Parse.User, () => { });
+ }).not.toThrow();
+ });
+
+ describe('Express-style cloud functions with (req, res) parameters', () => {
+ it('should support express-style cloud function with res.success()', async () => {
+ Parse.Cloud.define('expressStyleFunction', (req, res) => {
+ res.success({ message: 'Hello from express style!' });
+ });
+
+ const result = await Parse.Cloud.run('expressStyleFunction', {});
+ expect(result.message).toEqual('Hello from express style!');
+ });
+
+ it('should support express-style cloud function with res.error()', async () => {
+ Parse.Cloud.define('expressStyleError', (req, res) => {
+ res.error('Custom error message');
+ });
+
+ await expectAsync(Parse.Cloud.run('expressStyleError', {})).toBeRejectedWith(
+ new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Custom error message')
+ );
+ });
+
+ it('should support setting custom HTTP status code with res.status().success()', async () => {
+ Parse.Cloud.define('customStatusCode', (req, res) => {
+ res.status(201).success({ created: true });
+ });
+
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/customStatusCode',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ });
+
+ expect(response.status).toBe(201);
+ expect(response.data.result.created).toBe(true);
+ });
+
+ it('should support 401 unauthorized status code with error', async () => {
+ Parse.Cloud.define('unauthorizedFunction', (req, res) => {
+ if (!req.user) {
+ res.status(401).error('Unauthorized access');
+ } else {
+ res.success({ message: 'Authorized' });
+ }
+ });
+
+ await expectAsync(
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/unauthorizedFunction',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ })
+ ).toBeRejected();
+ });
+
+ it('should support 404 not found status code with error', async () => {
+ Parse.Cloud.define('notFoundFunction', (req, res) => {
+ res.status(404).error('Resource not found');
+ });
+
+ await expectAsync(
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/notFoundFunction',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ })
+ ).toBeRejected();
+ });
+
+ it('should default to 200 status code when not specified', async () => {
+ Parse.Cloud.define('defaultStatusCode', (req, res) => {
+ res.success({ message: 'Default status' });
+ });
+
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/defaultStatusCode',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.data.result.message).toBe('Default status');
+ });
+
+ it('should maintain backward compatibility with single-parameter functions', async () => {
+ Parse.Cloud.define('traditionalFunction', (req) => {
+ return { message: 'Traditional style works!' };
+ });
+
+ const result = await Parse.Cloud.run('traditionalFunction', {});
+ expect(result.message).toEqual('Traditional style works!');
+ });
+
+ it('should maintain backward compatibility with implicit return functions', async () => {
+ Parse.Cloud.define('implicitReturnFunction', () => 'Implicit return works!');
+
+ const result = await Parse.Cloud.run('implicitReturnFunction', {});
+ expect(result).toEqual('Implicit return works!');
+ });
+
+ it('should support async express-style functions', async () => {
+ Parse.Cloud.define('asyncExpressStyle', async (req, res) => {
+ await new Promise(resolve => setTimeout(resolve, 10));
+ res.success({ async: true });
+ });
+
+ const result = await Parse.Cloud.run('asyncExpressStyle', {});
+ expect(result.async).toBe(true);
+ });
+
+ it('should access request parameters in express-style functions', async () => {
+ Parse.Cloud.define('expressWithParams', (req, res) => {
+ const { name } = req.params;
+ res.success({ greeting: `Hello, ${name}!` });
+ });
+
+ const result = await Parse.Cloud.run('expressWithParams', { name: 'World' });
+ expect(result.greeting).toEqual('Hello, World!');
+ });
+
+ it('should access user in express-style functions', async () => {
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'testpass');
+ await user.signUp();
+
+ Parse.Cloud.define('expressWithUser', (req, res) => {
+ if (req.user) {
+ res.success({ username: req.user.get('username') });
+ } else {
+ res.status(401).error('Not authenticated');
+ }
+ });
+
+ const result = await Parse.Cloud.run('expressWithUser', {});
+ expect(result.username).toEqual('testuser');
+
+ await Parse.User.logOut();
+ });
+
+ it('should support setting custom headers with res.header()', async () => {
+ Parse.Cloud.define('customHeaderFunction', (req, res) => {
+ res.header('X-Custom-Header', 'custom-value').success({ message: 'OK' });
+ });
+
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/customHeaderFunction',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.headers['x-custom-header']).toBe('custom-value');
+ expect(response.data.result.message).toBe('OK');
+ });
+
+ it('should support setting multiple custom headers', async () => {
+ Parse.Cloud.define('multipleHeadersFunction', (req, res) => {
+ res.header('X-Header-One', 'value1')
+ .header('X-Header-Two', 'value2')
+ .success({ message: 'Multiple headers' });
+ });
+
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/multipleHeadersFunction',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.headers['x-header-one']).toBe('value1');
+ expect(response.headers['x-header-two']).toBe('value2');
+ expect(response.data.result.message).toBe('Multiple headers');
+ });
+
+ it('should support combining status code and custom headers', async () => {
+ Parse.Cloud.define('statusAndHeaderFunction', (req, res) => {
+ res.status(201)
+ .header('X-Resource-Id', '12345')
+ .success({ created: true });
+ });
+
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/statusAndHeaderFunction',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ json: true,
+ body: {},
+ });
+
+ expect(response.status).toBe(201);
+ expect(response.headers['x-resource-id']).toBe('12345');
+ expect(response.data.result.created).toBe(true);
+ });
});
});
diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js
index 145a6155b9..16d9d02950 100644
--- a/spec/CloudCodeLogger.spec.js
+++ b/spec/CloudCodeLogger.spec.js
@@ -1,67 +1,404 @@
-'use strict';
-var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
-var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
-
-describe("Cloud Code Logger", () => {
- it("should expose log to functions", (done) => {
- var logController = new LoggerController(new FileLoggerAdapter());
-
- Parse.Cloud.define("loggerTest", (req, res) => {
- req.log.info('logTest', 'info log', {info: 'some log' });
- req.log.error('logTest','error log', {error: 'there was an error'});
- res.success({});
- });
-
- Parse.Cloud.run('loggerTest').then(() => {
- return logController.getLogs({from: Date.now() - 500, size: 1000});
- }).then((res) => {
- expect(res.length).not.toBe(0);
- let lastLogs = res.slice(0, 3);
- let cloudFunctionMessage = lastLogs[0];
- let errorMessage = lastLogs[1];
- let infoMessage = lastLogs[2];
- expect(cloudFunctionMessage.level).toBe('info');
- expect(cloudFunctionMessage.params).toEqual({});
- expect(cloudFunctionMessage.message).toEqual('Ran cloud function loggerTest with:\nInput: {}\nResult: {}');
- expect(cloudFunctionMessage.functionName).toEqual('loggerTest');
- expect(errorMessage.level).toBe('error');
- expect(errorMessage.error).toBe('there was an error');
- expect(errorMessage.message).toBe('logTest error log');
- expect(infoMessage.level).toBe('info');
- expect(infoMessage.info).toBe('some log');
- expect(infoMessage.message).toBe('logTest info log');
- done();
- });
- });
-
- it("should expose log to trigger", (done) => {
- var logController = new LoggerController(new FileLoggerAdapter());
-
- Parse.Cloud.beforeSave("MyObject", (req, res) => {
- req.log.info('beforeSave MyObject', 'info log', {info: 'some log' });
- req.log.error('beforeSave MyObject','error log', {error: 'there was an error'});
- res.success({});
- });
-
- let obj = new Parse.Object('MyObject');
- obj.save().then(() => {
- return logController.getLogs({from: Date.now() - 500, size: 1000})
- }).then((res) => {
- expect(res.length).not.toBe(0);
- let lastLogs = res.slice(0, 3);
- let cloudTriggerMessage = lastLogs[0];
- let errorMessage = lastLogs[1];
- let infoMessage = lastLogs[2];
- expect(cloudTriggerMessage.level).toBe('info');
- expect(cloudTriggerMessage.input).toEqual({});
- expect(cloudTriggerMessage.message).toEqual('beforeSave triggered for MyObject\nInput: {}\nResult: {}');
- expect(errorMessage.level).toBe('error');
- expect(errorMessage.error).toBe('there was an error');
- expect(errorMessage.message).toBe('beforeSave MyObject error log');
- expect(infoMessage.level).toBe('info');
- expect(infoMessage.info).toBe('some log');
- expect(infoMessage.message).toBe('beforeSave MyObject info log');
- done();
- });
+const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
+const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
+ .WinstonLoggerAdapter;
+const fs = require('fs');
+const Config = require('../lib/Config');
+
+const loremFile = __dirname + '/support/lorem.txt';
+
+describe('Cloud Code Logger', () => {
+ let user;
+ let spy;
+ beforeEach(async () => {
+ Parse.User.enableUnsafeCurrentUser();
+ return reconfigureServer({
+ // useful to flip to false for fine tuning :).
+ silent: true,
+ logLevel: undefined,
+ logLevels: {
+ cloudFunctionError: 'error',
+ cloudFunctionSuccess: 'info',
+ triggerAfter: 'info',
+ triggerBeforeError: 'error',
+ triggerBeforeSuccess: 'info',
+ },
+ })
+ .then(() => {
+ return Parse.User.signUp('tester', 'abc')
+ .catch(() => { })
+ .then(loggedInUser => (user = loggedInUser))
+ .then(() => Parse.User.logIn(user.get('username'), 'abc'));
+ })
+ .then(() => {
+ spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
+ });
+ });
+
+ // Note that helpers takes care of logout.
+ // see helpers.js:afterEach
+
+ it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)('should expose log to functions', () => {
+ const spy = spyOn(Config.get('test').loggerController, 'log').and.callThrough();
+ Parse.Cloud.define('loggerTest', req => {
+ req.log.info('logTest', 'info log', { info: 'some log' });
+ req.log.error('logTest', 'error log', { error: 'there was an error' });
+ return {};
+ });
+
+ return Parse.Cloud.run('loggerTest').then(() => {
+ expect(spy).toHaveBeenCalledTimes(3);
+ const cloudFunctionMessage = spy.calls.all()[2];
+ const errorMessage = spy.calls.all()[1];
+ const infoMessage = spy.calls.all()[0];
+ expect(cloudFunctionMessage.args[0]).toBe('info');
+ expect(cloudFunctionMessage.args[1][1].params).toEqual({});
+ expect(cloudFunctionMessage.args[1][0]).toMatch(
+ /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/
+ );
+ expect(cloudFunctionMessage.args[1][1].functionName).toEqual('loggerTest');
+ expect(errorMessage.args[0]).toBe('error');
+ expect(errorMessage.args[1][2].error).toBe('there was an error');
+ expect(errorMessage.args[1][0]).toBe('logTest');
+ expect(errorMessage.args[1][1]).toBe('error log');
+ expect(infoMessage.args[0]).toBe('info');
+ expect(infoMessage.args[1][2].info).toBe('some log');
+ expect(infoMessage.args[1][0]).toBe('logTest');
+ expect(infoMessage.args[1][1]).toBe('info log');
+ });
+ });
+
+ it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)('trigger should obfuscate password', done => {
+ Parse.Cloud.beforeSave(Parse.User, req => {
+ return req.object;
+ });
+
+ Parse.User.signUp('tester123', 'abc')
+ .then(() => {
+ const entry = spy.calls.mostRecent().args;
+ expect(entry[1]).not.toMatch(/password":"abc/);
+ expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/);
+ done();
+ })
+ .then(null, e => done.fail(e));
+ });
+
+ it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)('should expose log to trigger', done => {
+ Parse.Cloud.beforeSave('MyObject', req => {
+ req.log.info('beforeSave MyObject', 'info log', { info: 'some log' });
+ req.log.error('beforeSave MyObject', 'error log', {
+ error: 'there was an error',
+ });
+ return {};
+ });
+
+ const obj = new Parse.Object('MyObject');
+ obj.save().then(() => {
+ const lastCalls = spy.calls.all().reverse();
+ const cloudTriggerMessage = lastCalls[0].args;
+ const errorMessage = lastCalls[1].args;
+ const infoMessage = lastCalls[2].args;
+ expect(cloudTriggerMessage[0]).toBe('info');
+ expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave');
+ expect(cloudTriggerMessage[1]).toMatch(
+ /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/
+ );
+ expect(cloudTriggerMessage[2].user).toBe(user.id);
+ expect(errorMessage[0]).toBe('error');
+ expect(errorMessage[3].error).toBe('there was an error');
+ expect(errorMessage[1] + ' ' + errorMessage[2]).toBe('beforeSave MyObject error log');
+ expect(infoMessage[0]).toBe('info');
+ expect(infoMessage[3].info).toBe('some log');
+ expect(infoMessage[1] + ' ' + infoMessage[2]).toBe('beforeSave MyObject info log');
+ done();
+ });
+ });
+
+ it('should truncate really long lines when asked to', () => {
+ const logController = new LoggerController(new WinstonLoggerAdapter());
+ const longString = fs.readFileSync(loremFile, 'utf8');
+ const truncatedString = logController.truncateLogMessage(longString);
+ expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)'
+ });
+
+ it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)('should truncate input and result of long lines', done => {
+ const longString = fs.readFileSync(loremFile, 'utf8');
+ Parse.Cloud.define('aFunction', req => {
+ return req.params;
+ });
+
+ Parse.Cloud.run('aFunction', { longString })
+ .then(() => {
+ const log = spy.calls.mostRecent().args;
+ expect(log[0]).toEqual('info');
+ expect(log[1]).toMatch(
+ /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m
+ );
+ done();
+ })
+ .then(null, e => done.fail(e));
+ });
+
+ it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => {
+ Parse.Cloud.afterSave('MyObject', () => { });
+ new Parse.Object('MyObject')
+ .save()
+ .then(() => {
+ const log = spy.calls.mostRecent().args;
+ expect(log[2].triggerType).toEqual('afterSave');
+ done();
+ })
+ // catch errors - not that the error is actually useful :(
+ .then(null, e => done.fail(e));
+ });
+
+ it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)('should log a denied beforeSave', done => {
+ Parse.Cloud.beforeSave('MyObject', () => {
+ throw 'uh oh!';
+ });
+
+ new Parse.Object('MyObject')
+ .save()
+ .then(
+ () => done.fail('this is not supposed to succeed'),
+ () => new Promise(resolve => setTimeout(resolve, 100))
+ )
+ .then(() => {
+ const logs = spy.calls.all().reverse();
+ const log = logs[1].args; // 0 is the 'uh oh!' from rejection...
+ expect(log[0]).toEqual('error');
+ const error = log[2].error;
+ expect(error instanceof Parse.Error).toBeTruthy();
+ expect(error.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(error.message).toBe('uh oh!');
+ done();
+ });
+ });
+
+ it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)('should log cloud function success', done => {
+ Parse.Cloud.define('aFunction', () => {
+ return 'it worked!';
+ });
+
+ Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => {
+ const log = spy.calls.mostRecent().args;
+ expect(log[0]).toEqual('info');
+ expect(log[1]).toMatch(
+ /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/
+ );
+ done();
+ });
+ });
+
+ it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async () => {
+ Parse.Cloud.define('aFunction', () => {
+ return 'it worked!';
+ });
+
+ Parse.Cloud.define('bFunction', () => {
+ throw new Error('Failed');
+ });
+
+ await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => {
+ const log = spy.calls.allArgs().find(log => log[1].startsWith('Ran cloud function '))?.[0];
+ expect(log).toEqual('info');
+ });
+
+ Parse.Cloud._removeAllHooks();
+ await reconfigureServer({
+ silent: true,
+ logLevels: {
+ cloudFunctionSuccess: 'warn',
+ cloudFunctionError: 'info',
+ },
+ });
+
+ Parse.Cloud.define('bFunction', () => {
+ throw new Error('Failed');
+ });
+
+ spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
+
+ try {
+ await Parse.Cloud.run('bFunction', { foo: 'bar' });
+ throw new Error('bFunction should have failed');
+ } catch {
+ const log = spy.calls
+ .allArgs()
+ .find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0];
+ expect(log).toEqual('info');
+ }
+ });
+
+ it('should log cloud function triggers using the custom log level', async () => {
+ const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => {
+ Parse.Cloud._removeAllHooks();
+ await reconfigureServer({
+ silent: true,
+ logLevel,
+ logLevels: {
+ triggerAfter,
+ triggerBeforeSuccess,
+ },
+ });
+
+ let afterSaveResolve;
+ const afterSavePromise = new Promise(resolve => { afterSaveResolve = resolve; });
+ Parse.Cloud.beforeSave('TestClass', () => { });
+ Parse.Cloud.afterSave('TestClass', () => { afterSaveResolve(); });
+
+ spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
+ const obj = new Parse.Object('TestClass');
+ await obj.save();
+ await afterSavePromise;
+
+ return {
+ beforeSave: spy.calls
+ .allArgs()
+ .find(log => log[1].startsWith('beforeSave triggered for TestClass for user '))?.[0],
+ afterSave: spy.calls
+ .allArgs()
+ .find(log => log[1].startsWith('afterSave triggered for TestClass for user '))?.[0],
+ };
+ };
+
+ let calls = await execTest('silly', 'silly', 'debug');
+ expect(calls).toEqual({ beforeSave: 'silly', afterSave: 'debug' });
+
+ calls = await execTest('info', 'warn', 'debug');
+ expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined });
+ });
+
+ it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)('should log cloud function failure', done => {
+ Parse.Cloud.define('aFunction', () => {
+ throw 'it failed!';
+ });
+
+ Parse.Cloud.run('aFunction', { foo: 'bar' })
+ .catch(() => { })
+ .then(() => {
+ const logs = spy.calls.all().reverse();
+ expect(logs[0].args[1]).toBe('Parse error: ');
+ expect(logs[0].args[2].message).toBe('it failed!');
+
+ const log = logs[1].args;
+ expect(log[0]).toEqual('error');
+ expect(log[1]).toMatch(
+ /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/
+ );
+ const errorString = JSON.stringify(
+ new Parse.Error(Parse.Error.SCRIPT_FAILED, 'it failed!')
+ );
+ expect(log[1].indexOf(errorString)).toBeGreaterThan(0);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ xit('should log a changed beforeSave indicating a change', done => {
+ pending('needs more work.....');
+ const logController = new LoggerController(new WinstonLoggerAdapter());
+
+ Parse.Cloud.beforeSave('MyObject', req => {
+ const myObj = req.object;
+ myObj.set('aChange', true);
+ return myObj;
+ });
+
+ new Parse.Object('MyObject')
+ .save()
+ .then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
+ .then(() => {
+ // expect the log to indicate that it has changed
+ /*
+ Here's what it looks like on parse.com...
+
+ Input: {"original":{"clientVersion":"1","createdAt":"2016-06-02T05:29:08.694Z","image":{"__type":"File","name":"tfss-xxxxxxxx.png","url":"http://files.parsetfss.com/xxxxxxxx.png"},"lastScanDate":{"__type":"Date","iso":"2016-06-02T05:28:58.135Z"},"localIdentifier":"XXXXX","objectId":"OFHMX7ZUcI","status":... (truncated)
+ Result: Update changed to {"object":{"__type":"Pointer","className":"Emoticode","objectId":"ksrq7z3Ehc"},"imageThumb":{"__type":"File","name":"tfss-xxxxxxx.png","url":"http://files.parsetfss.com/xxxxx.png"},"status":"success"}
+ */
+ done();
+ })
+ .then(null, e => done.fail(JSON.stringify(e)));
+ });
+
+ it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)('cloud function should obfuscate password', done => {
+ Parse.Cloud.define('testFunction', () => {
+ return 'verify code success';
+ });
+
+ Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' })
+ .then(() => {
+ const entry = spy.calls.mostRecent().args;
+ expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/);
+ done();
+ })
+ .then(null, e => done.fail(e));
+ });
+
+ it('should only log once for object not found', async () => {
+ const config = Config.get('test');
+ const spy = spyOn(config.loggerController, 'error').and.callThrough();
+ try {
+ const object = new Parse.Object('Object');
+ object.id = 'invalid';
+ await object.fetch();
+ } catch (e) {
+ /**/
+ }
+ expect(spy).toHaveBeenCalled();
+ expect(spy.calls.count()).toBe(1);
+ const { args } = spy.calls.mostRecent();
+ expect(args[0]).toBe('Parse error: ');
+ expect(args[1].message).toBe('Object not found.');
+ });
+
+ it('should log cloud function execution using the silent log level', async () => {
+ Parse.Cloud._removeAllHooks();
+ await reconfigureServer({
+ logLevels: {
+ cloudFunctionSuccess: 'silent',
+ cloudFunctionError: 'silent',
+ },
+ });
+ Parse.Cloud.define('aFunction', () => {
+ return 'it worked!';
+ });
+ Parse.Cloud.define('bFunction', () => {
+ throw new Error('Failed');
});
+ spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
+
+ await Parse.Cloud.run('aFunction', { foo: 'bar' });
+ expect(spy).toHaveBeenCalledTimes(0);
+
+ await expectAsync(Parse.Cloud.run('bFunction', { foo: 'bar' })).toBeRejected();
+ // Not "Failed running cloud function message..."
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it('should log cloud function triggers using the silent log level', async () => {
+ Parse.Cloud._removeAllHooks();
+ await reconfigureServer({
+ logLevels: {
+ triggerAfter: 'silent',
+ triggerBeforeSuccess: 'silent',
+ triggerBeforeError: 'silent',
+ },
+ });
+ Parse.Cloud.beforeSave('TestClassError', () => {
+ throw new Error('Failed');
+ });
+ Parse.Cloud.beforeSave('TestClass', () => { });
+ Parse.Cloud.afterSave('TestClass', () => { });
+
+ spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
+
+ const obj = new Parse.Object('TestClass');
+ await obj.save();
+ expect(spy).toHaveBeenCalledTimes(0);
+
+ const objError = new Parse.Object('TestClassError');
+ await expectAsync(objError.save()).toBeRejected();
+ // Not "beforeSave failed for TestClassError for user ..."
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
});
diff --git a/spec/CloudCodeMultipart.spec.js b/spec/CloudCodeMultipart.spec.js
new file mode 100644
index 0000000000..b2f60c0761
--- /dev/null
+++ b/spec/CloudCodeMultipart.spec.js
@@ -0,0 +1,366 @@
+'use strict';
+const http = require('http');
+
+function postMultipart(url, headers, body) {
+ return new Promise((resolve, reject) => {
+ const parsed = new URL(url);
+ const req = http.request(
+ {
+ method: 'POST',
+ hostname: parsed.hostname,
+ port: parsed.port,
+ path: parsed.pathname,
+ headers,
+ },
+ res => {
+ const chunks = [];
+ res.on('data', chunk => chunks.push(chunk));
+ res.on('end', () => {
+ const raw = Buffer.concat(chunks).toString();
+ try {
+ resolve({ status: res.statusCode, data: JSON.parse(raw) });
+ } catch {
+ resolve({ status: res.statusCode, data: raw });
+ }
+ });
+ }
+ );
+ req.on('error', reject);
+ req.write(body);
+ req.end();
+ });
+}
+
+function buildMultipartBody(boundary, parts) {
+ const segments = [];
+ for (const part of parts) {
+ segments.push(`--${boundary}\r\n`);
+ if (part.filename) {
+ segments.push(
+ `Content-Disposition: form-data; name="${part.name}"; filename="${part.filename}"\r\n`
+ );
+ segments.push(`Content-Type: ${part.contentType || 'application/octet-stream'}\r\n\r\n`);
+ segments.push(part.data);
+ } else {
+ segments.push(`Content-Disposition: form-data; name="${part.name}"\r\n\r\n`);
+ segments.push(part.value);
+ }
+ segments.push('\r\n');
+ }
+ segments.push(`--${boundary}--\r\n`);
+ return Buffer.concat(segments.map(s => (typeof s === 'string' ? Buffer.from(s) : s)));
+}
+
+describe('Cloud Code Multipart', () => {
+ it('should not reject multipart requests at the JSON parser level', async () => {
+ Parse.Cloud.define('multipartTest', req => {
+ return { received: true };
+ });
+
+ const boundary = '----TestBoundary123';
+ const body = buildMultipartBody(boundary, [
+ { name: 'key', value: 'value' },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartTest`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).not.toBe(400);
+ });
+
+ it('should parse text fields from multipart request', async () => {
+ Parse.Cloud.define('multipartText', req => {
+ return { userId: req.params.userId, count: req.params.count };
+ });
+
+ const boundary = '----TestBoundary456';
+ const body = buildMultipartBody(boundary, [
+ { name: 'userId', value: 'abc123' },
+ { name: 'count', value: '5' },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartText`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.userId).toBe('abc123');
+ expect(result.data.result.count).toBe('5');
+ });
+
+ it('should parse file fields from multipart request', async () => {
+ Parse.Cloud.define('multipartFile', req => {
+ const file = req.params.avatar;
+ return {
+ filename: file.filename,
+ contentType: file.contentType,
+ size: file.data.length,
+ content: file.data.toString('utf8'),
+ };
+ });
+
+ const boundary = '----TestBoundary789';
+ const fileContent = Buffer.from('hello world');
+ const body = buildMultipartBody(boundary, [
+ { name: 'avatar', filename: 'photo.txt', contentType: 'text/plain', data: fileContent },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartFile`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.filename).toBe('photo.txt');
+ expect(result.data.result.contentType).toBe('text/plain');
+ expect(result.data.result.size).toBe(11);
+ expect(result.data.result.content).toBe('hello world');
+ });
+
+ it('should parse mixed text and file fields from multipart request', async () => {
+ Parse.Cloud.define('multipartMixed', req => {
+ return {
+ userId: req.params.userId,
+ hasAvatar: !!req.params.avatar,
+ avatarFilename: req.params.avatar.filename,
+ };
+ });
+
+ const boundary = '----TestBoundaryMixed';
+ const body = buildMultipartBody(boundary, [
+ { name: 'userId', value: 'user42' },
+ { name: 'avatar', filename: 'img.jpg', contentType: 'image/jpeg', data: Buffer.from([0xff, 0xd8, 0xff]) },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartMixed`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.userId).toBe('user42');
+ expect(result.data.result.hasAvatar).toBe(true);
+ expect(result.data.result.avatarFilename).toBe('img.jpg');
+ });
+
+ it('should parse multiple file fields from multipart request', async () => {
+ Parse.Cloud.define('multipartMultiFile', req => {
+ return {
+ file1Name: req.params.doc1.filename,
+ file2Name: req.params.doc2.filename,
+ file1Size: req.params.doc1.data.length,
+ file2Size: req.params.doc2.data.length,
+ };
+ });
+
+ const boundary = '----TestBoundaryMulti';
+ const body = buildMultipartBody(boundary, [
+ { name: 'doc1', filename: 'a.txt', contentType: 'text/plain', data: Buffer.from('aaa') },
+ { name: 'doc2', filename: 'b.txt', contentType: 'text/plain', data: Buffer.from('bbbbb') },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartMultiFile`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.file1Name).toBe('a.txt');
+ expect(result.data.result.file2Name).toBe('b.txt');
+ expect(result.data.result.file1Size).toBe(3);
+ expect(result.data.result.file2Size).toBe(5);
+ });
+
+ it('should handle empty file field from multipart request', async () => {
+ Parse.Cloud.define('multipartEmptyFile', req => {
+ return {
+ filename: req.params.empty.filename,
+ size: req.params.empty.data.length,
+ };
+ });
+
+ const boundary = '----TestBoundaryEmpty';
+ const body = buildMultipartBody(boundary, [
+ { name: 'empty', filename: 'empty.bin', contentType: 'application/octet-stream', data: Buffer.alloc(0) },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartEmptyFile`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.filename).toBe('empty.bin');
+ expect(result.data.result.size).toBe(0);
+ });
+
+ it('should still handle JSON requests as before', async () => {
+ Parse.Cloud.define('jsonTest', req => {
+ return { name: req.params.name, count: req.params.count };
+ });
+
+ const result = await Parse.Cloud.run('jsonTest', { name: 'hello', count: 42 });
+
+ expect(result.name).toBe('hello');
+ expect(result.count).toBe(42);
+ });
+
+ it('should reject multipart request exceeding maxUploadSize', async () => {
+ await reconfigureServer({ maxUploadSize: '1kb' });
+
+ Parse.Cloud.define('multipartLarge', req => {
+ return { ok: true };
+ });
+
+ const boundary = '----TestBoundaryLarge';
+ const largeData = Buffer.alloc(2 * 1024, 'x');
+ const body = buildMultipartBody(boundary, [
+ { name: 'bigfile', filename: 'large.bin', contentType: 'application/octet-stream', data: largeData },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartLarge`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.data.code).toBe(Parse.Error.OBJECT_TOO_LARGE);
+ });
+
+ it('should reject multipart request exceeding maxUploadSize via file stream', async () => {
+ await reconfigureServer({ maxUploadSize: '1kb' });
+
+ Parse.Cloud.define('multipartLargeFile', req => {
+ return { ok: true };
+ });
+
+ const boundary = '----TestBoundaryLargeFile';
+ const body = buildMultipartBody(boundary, [
+ { name: 'small', value: 'ok' },
+ { name: 'bigfile', filename: 'large.bin', contentType: 'application/octet-stream', data: Buffer.alloc(2 * 1024, 'x') },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartLargeFile`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.data.code).toBe(Parse.Error.OBJECT_TOO_LARGE);
+ });
+
+ it('should reject malformed multipart body', async () => {
+ Parse.Cloud.define('multipartMalformed', req => {
+ return { ok: true };
+ });
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartMalformed`,
+ {
+ 'Content-Type': 'multipart/form-data; boundary=----TestBoundaryBad',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ Buffer.from('this is not valid multipart data')
+ );
+
+ expect(result.data.code).toBe(Parse.Error.INVALID_JSON);
+ });
+
+ it('should not allow prototype pollution via __proto__ field name', async () => {
+ Parse.Cloud.define('multipartProto', req => {
+ const obj = {};
+ return {
+ polluted: obj.polluted !== undefined,
+ paramsClean: Object.getPrototypeOf(req.params) === Object.prototype,
+ };
+ });
+
+ const boundary = '----TestBoundaryProto';
+ const body = buildMultipartBody(boundary, [
+ { name: '__proto__', value: '{"polluted":"yes"}' },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartProto`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.polluted).toBe(false);
+ expect(result.data.result.paramsClean).toBe(true);
+ });
+
+ it('should not grant master key access via multipart fields', async () => {
+ const obj = new Parse.Object('SecretClass');
+ await obj.save(null, { useMasterKey: true });
+
+ Parse.Cloud.define('multipartAuthCheck', req => {
+ return { isMaster: req.master };
+ });
+
+ const boundary = '----TestBoundaryAuth';
+ const body = buildMultipartBody(boundary, [
+ { name: '_MasterKey', value: 'test' },
+ ]);
+
+ const result = await postMultipart(
+ `http://localhost:8378/1/functions/multipartAuthCheck`,
+ {
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body
+ );
+
+ expect(result.status).toBe(200);
+ expect(result.data.result.isMaster).toBe(false);
+ });
+});
diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js
new file mode 100644
index 0000000000..dc2f84bc5f
--- /dev/null
+++ b/spec/DatabaseController.spec.js
@@ -0,0 +1,968 @@
+const Config = require('../lib/Config');
+const DatabaseController = require('../lib/Controllers/DatabaseController.js');
+const validateQuery = DatabaseController._validateQuery;
+
+describe('DatabaseController', function () {
+ describe('validateQuery', function () {
+ it('should not restructure simple cases of SERVER-13732', done => {
+ const query = {
+ $or: [{ a: 1 }, { a: 2 }],
+ _rperm: { $in: ['a', 'b'] },
+ foo: 3,
+ };
+ validateQuery(query);
+ expect(query).toEqual({
+ $or: [{ a: 1 }, { a: 2 }],
+ _rperm: { $in: ['a', 'b'] },
+ foo: 3,
+ });
+ done();
+ });
+
+ it('should not restructure SERVER-13732 queries with $nears', done => {
+ let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } };
+ validateQuery(query);
+ expect(query).toEqual({
+ $or: [{ a: 1 }, { b: 1 }],
+ c: { $nearSphere: {} },
+ });
+ query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } };
+ validateQuery(query);
+ expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } });
+ done();
+ });
+
+ it('should not push refactored keys down a tree for SERVER-13732', done => {
+ const query = {
+ a: 1,
+ $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
+ };
+ validateQuery(query);
+ expect(query).toEqual({
+ a: 1,
+ $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
+ });
+
+ done();
+ });
+
+ it('should reject invalid queries', done => {
+ expect(() => validateQuery({ $or: { a: 1 } })).toThrow();
+ done();
+ });
+
+ it('should accept valid queries', done => {
+ expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow();
+ done();
+ });
+ });
+
+ describe('addPointerPermissions', function () {
+ const CLASS_NAME = 'Foo';
+ const USER_ID = 'userId';
+ const ACL_GROUP = [USER_ID];
+ const OPERATION = 'find';
+
+ const databaseController = new DatabaseController();
+ const schemaController = jasmine.createSpyObj('SchemaController', [
+ 'testPermissionsForClassName',
+ 'getClassLevelPermissions',
+ 'getExpectedType',
+ ]);
+
+ it('should not decorate query if no pointer CLPs are present', done => {
+ const clp = buildCLP();
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(true);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({ ...query });
+
+ done();
+ });
+
+ it('should decorate query if a pointer CLP entry is present', done => {
+ const clp = buildCLP(['user']);
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Pointer' });
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({ ...query, user: createUserPointer(USER_ID) });
+
+ done();
+ });
+
+ it('should decorate query if an array CLP entry is present', done => {
+ const clp = buildCLP(['users']);
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'users')
+ .and.returnValue({ type: 'Array' });
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({
+ ...query,
+ users: { $all: [createUserPointer(USER_ID)] },
+ });
+
+ done();
+ });
+
+ it('should decorate query if an object CLP entry is present', done => {
+ const clp = buildCLP(['user']);
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Object' });
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({
+ ...query,
+ user: createUserPointer(USER_ID),
+ });
+
+ done();
+ });
+
+ it('should decorate query if a pointer CLP is present and the same field is part of the query', done => {
+ const clp = buildCLP(['user']);
+ const query = { a: 'b', user: 'a' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Pointer' });
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({
+ $and: [{ user: createUserPointer(USER_ID) }, { ...query }],
+ });
+
+ done();
+ });
+
+ it('should transform the query to an $or query if multiple array/pointer CLPs are present', done => {
+ const clp = buildCLP(['user', 'users', 'userObject']);
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Pointer' });
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'users')
+ .and.returnValue({ type: 'Array' });
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'userObject')
+ .and.returnValue({ type: 'Object' });
+
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+
+ expect(output).toEqual({
+ $or: [
+ { ...query, user: createUserPointer(USER_ID) },
+ { ...query, users: { $all: [createUserPointer(USER_ID)] } },
+ { ...query, userObject: createUserPointer(USER_ID) },
+ ],
+ });
+
+ done();
+ });
+
+ it('should not return a $or operation if the query involves one of the two fields also used as array/pointer permissions', done => {
+ const clp = buildCLP(['users', 'user']);
+ const query = { a: 'b', user: createUserPointer(USER_ID) };
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Pointer' });
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'users')
+ .and.returnValue({ type: 'Array' });
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+ expect(output).toEqual({ ...query, user: createUserPointer(USER_ID) });
+ done();
+ });
+
+ it('should not return a $or operation if the query involves one of the fields also used as array/pointer permissions', done => {
+ const clp = buildCLP(['user', 'users', 'userObject']);
+ const query = { a: 'b', user: createUserPointer(USER_ID) };
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Pointer' });
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'users')
+ .and.returnValue({ type: 'Array' });
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'userObject')
+ .and.returnValue({ type: 'Object' });
+ const output = databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+ expect(output).toEqual({ ...query, user: createUserPointer(USER_ID) });
+ done();
+ });
+
+ it('should throw an error if for some unexpected reason the property specified in the CLP is neither a pointer nor an array', done => {
+ const clp = buildCLP(['user']);
+ const query = { a: 'b' };
+
+ schemaController.testPermissionsForClassName
+ .withArgs(CLASS_NAME, ACL_GROUP, OPERATION)
+ .and.returnValue(false);
+ schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp);
+ schemaController.getExpectedType
+ .withArgs(CLASS_NAME, 'user')
+ .and.returnValue({ type: 'Number' });
+
+ expect(() => {
+ databaseController.addPointerPermissions(
+ schemaController,
+ CLASS_NAME,
+ OPERATION,
+ query,
+ ACL_GROUP
+ );
+ }).toThrow(
+ Error(
+ `An unexpected condition occurred when resolving pointer permissions: ${CLASS_NAME} user`
+ )
+ );
+
+ done();
+ });
+ });
+
+ describe('reduceOperations', function () {
+ const databaseController = new DatabaseController();
+
+ it('objectToEntriesStrings', done => {
+ const output = databaseController.objectToEntriesStrings({ a: 1, b: 2, c: 3 });
+ expect(output).toEqual(['"a":1', '"b":2', '"c":3']);
+ done();
+ });
+
+ it('reduceOrOperation', done => {
+ expect(databaseController.reduceOrOperation({ a: 1 })).toEqual({ a: 1 });
+ expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] })).toEqual({
+ $or: [{ a: 1 }, { b: 2 }],
+ });
+ expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 2 }] })).toEqual({
+ $or: [{ a: 1 }, { a: 2 }],
+ });
+ expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 1 }] })).toEqual({ a: 1 });
+ expect(
+ databaseController.reduceOrOperation({ $or: [{ a: 1, b: 2, c: 3 }, { a: 1 }] })
+ ).toEqual({ a: 1 });
+ expect(
+ databaseController.reduceOrOperation({ $or: [{ b: 2 }, { a: 1, b: 2, c: 3 }] })
+ ).toEqual({ b: 2 });
+ done();
+ });
+
+ it('reduceAndOperation', done => {
+ expect(databaseController.reduceAndOperation({ a: 1 })).toEqual({ a: 1 });
+ expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] })).toEqual({
+ $and: [{ a: 1 }, { b: 2 }],
+ });
+ expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 2 }] })).toEqual({
+ $and: [{ a: 1 }, { a: 2 }],
+ });
+ expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 1 }] })).toEqual({
+ a: 1,
+ });
+ expect(
+ databaseController.reduceAndOperation({ $and: [{ a: 1, b: 2, c: 3 }, { b: 2 }] })
+ ).toEqual({ a: 1, b: 2, c: 3 });
+ done();
+ });
+ });
+
+ describe('enableCollationCaseComparison', () => {
+ const dummyStorageAdapter = {
+ find: () => Promise.resolve([]),
+ watch: () => Promise.resolve(),
+ getAllClasses: () => Promise.resolve([]),
+ };
+
+ beforeEach(() => {
+ Config.get(Parse.applicationId).schemaCache.clear();
+ });
+
+ it('should force caseInsensitive to false with enableCollationCaseComparison option', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {
+ enableCollationCaseComparison: true,
+ });
+ const spy = spyOn(dummyStorageAdapter, 'find');
+ spy.and.callThrough();
+ await databaseController.find('SomeClass', {}, { caseInsensitive: true });
+ expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(false);
+ });
+
+ it('should support caseInsensitive without enableCollationCaseComparison option', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {});
+ const spy = spyOn(dummyStorageAdapter, 'find');
+ spy.and.callThrough();
+ await databaseController.find('_User', {}, { caseInsensitive: true });
+ expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(true);
+ });
+
+ it_only_db('mongo')(
+ 'should create insensitive indexes without enableCollationCaseComparison',
+ async () => {
+ await reconfigureServer({
+ databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonFalse',
+ databaseAdapter: undefined,
+ });
+ const user = new Parse.User();
+ await user.save({
+ username: 'example',
+ password: 'password',
+ email: 'example@example.com',
+ });
+ const schemas = await Parse.Schema.all();
+ const UserSchema = schemas.find(({ className }) => className === '_User');
+ expect(UserSchema.indexes).toEqual({
+ _id_: { _id: 1 },
+ username_1: { username: 1 },
+ case_insensitive_username: { username: 1 },
+ case_insensitive_email: { email: 1 },
+ email_1: { email: 1 },
+ _email_verify_token: { _email_verify_token: 1 },
+ _perishable_token: { _perishable_token: 1 },
+ _auth_data_custom_id: { '_auth_data_custom.id': 1 },
+ _auth_data_facebook_id: { '_auth_data_facebook.id': 1 },
+ _auth_data_myoauth_id: { '_auth_data_myoauth.id': 1 },
+ _auth_data_shortLivedAuth_id: { '_auth_data_shortLivedAuth.id': 1 },
+ _auth_data_anonymous_id: { '_auth_data_anonymous.id': 1 },
+ });
+ }
+ );
+
+ it_only_db('mongo')(
+ 'should not create insensitive indexes with enableCollationCaseComparison',
+ async () => {
+ await reconfigureServer({
+ enableCollationCaseComparison: true,
+ databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonTrue',
+ databaseAdapter: undefined,
+ });
+ const user = new Parse.User();
+ await user.save({
+ username: 'example',
+ password: 'password',
+ email: 'example@example.com',
+ });
+ const schemas = await Parse.Schema.all();
+ const UserSchema = schemas.find(({ className }) => className === '_User');
+ expect(UserSchema.indexes).toEqual({
+ _id_: { _id: 1 },
+ username_1: { username: 1 },
+ email_1: { email: 1 },
+ _email_verify_token: { _email_verify_token: 1 },
+ _perishable_token: { _perishable_token: 1 },
+ _auth_data_custom_id: { '_auth_data_custom.id': 1 },
+ _auth_data_facebook_id: { '_auth_data_facebook.id': 1 },
+ _auth_data_myoauth_id: { '_auth_data_myoauth.id': 1 },
+ _auth_data_shortLivedAuth_id: { '_auth_data_shortLivedAuth.id': 1 },
+ _auth_data_anonymous_id: { '_auth_data_anonymous.id': 1 },
+ });
+ }
+ );
+
+ it_only_db('mongo')(
+ 'should use _email_verify_token index in email verification',
+ async () => {
+ const TestUtils = require('../lib/TestUtils');
+ let emailVerificationLink;
+ const emailSentPromise = TestUtils.resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ emailVerificationLink = options.link;
+ emailSentPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ databaseURI: 'mongodb://localhost:27017/testEmailVerifyTokenIndexStats',
+ databaseAdapter: undefined,
+ appName: 'test',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ // Create a user to trigger email verification
+ const user = new Parse.User();
+ user.setUsername('statsuser');
+ user.setPassword('password');
+ user.set('email', 'stats@example.com');
+ await user.signUp();
+ await emailSentPromise;
+
+ // Get index stats before the query
+ const config = Config.get(Parse.applicationId);
+ const collection = await config.database.adapter._adaptiveCollection('_User');
+ const statsBefore = await collection._mongoCollection.aggregate([
+ { $indexStats: {} },
+ ]).toArray();
+ const emailVerifyIndexBefore = statsBefore.find(
+ stat => stat.name === '_email_verify_token'
+ );
+ const accessesBefore = emailVerifyIndexBefore?.accesses?.ops || 0;
+
+ // Perform email verification (this should use the index)
+ const request = require('../lib/request');
+ await request({
+ url: emailVerificationLink,
+ followRedirects: false,
+ });
+
+ // Get index stats after the query
+ const statsAfter = await collection._mongoCollection.aggregate([
+ { $indexStats: {} },
+ ]).toArray();
+ const emailVerifyIndexAfter = statsAfter.find(
+ stat => stat.name === '_email_verify_token'
+ );
+ const accessesAfter = emailVerifyIndexAfter?.accesses?.ops || 0;
+
+ // Verify the index was actually used
+ expect(accessesAfter).toBeGreaterThan(accessesBefore);
+ expect(emailVerifyIndexAfter).toBeDefined();
+
+ // Verify email verification succeeded
+ await user.fetch();
+ expect(user.get('emailVerified')).toBe(true);
+ }
+ );
+
+ it_only_db('mongo')(
+ 'should use _perishable_token index in password reset',
+ async () => {
+ const TestUtils = require('../lib/TestUtils');
+ let passwordResetLink;
+ const emailSentPromise = TestUtils.resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: options => {
+ passwordResetLink = options.link;
+ emailSentPromise.resolve();
+ },
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ databaseURI: 'mongodb://localhost:27017/testPerishableTokenIndexStats',
+ databaseAdapter: undefined,
+ appName: 'test',
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ // Create a user
+ const user = new Parse.User();
+ user.setUsername('statsuser2');
+ user.setPassword('oldpassword');
+ user.set('email', 'stats2@example.com');
+ await user.signUp();
+
+ // Request password reset
+ await Parse.User.requestPasswordReset('stats2@example.com');
+ await emailSentPromise;
+
+ const url = new URL(passwordResetLink);
+ const token = url.searchParams.get('token');
+
+ // Get index stats before the query
+ const config = Config.get(Parse.applicationId);
+ const collection = await config.database.adapter._adaptiveCollection('_User');
+ const statsBefore = await collection._mongoCollection.aggregate([
+ { $indexStats: {} },
+ ]).toArray();
+ const perishableTokenIndexBefore = statsBefore.find(
+ stat => stat.name === '_perishable_token'
+ );
+ const accessesBefore = perishableTokenIndexBefore?.accesses?.ops || 0;
+
+ // Perform password reset (this should use the index)
+ const request = require('../lib/request');
+ await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/apps/test/request_password_reset',
+ body: { new_password: 'newpassword', token, username: 'statsuser2' },
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ followRedirects: false,
+ });
+
+ // Get index stats after the query
+ const statsAfter = await collection._mongoCollection.aggregate([
+ { $indexStats: {} },
+ ]).toArray();
+ const perishableTokenIndexAfter = statsAfter.find(
+ stat => stat.name === '_perishable_token'
+ );
+ const accessesAfter = perishableTokenIndexAfter?.accesses?.ops || 0;
+
+ // Verify the index was actually used
+ expect(accessesAfter).toBeGreaterThan(accessesBefore);
+ expect(perishableTokenIndexAfter).toBeDefined();
+ }
+ );
+ });
+
+ describe('convertEmailToLowercase', () => {
+ const dummyStorageAdapter = {
+ createObject: () => Promise.resolve({ ops: [{}] }),
+ findOneAndUpdate: () => Promise.resolve({}),
+ watch: () => Promise.resolve(),
+ getAllClasses: () =>
+ Promise.resolve([
+ {
+ className: '_User',
+ fields: { email: 'String' },
+ indexes: {},
+ classLevelPermissions: { protectedFields: {} },
+ },
+ ]),
+ };
+ const dates = {
+ createdAt: { iso: undefined, __type: 'Date' },
+ updatedAt: { iso: undefined, __type: 'Date' },
+ };
+
+ it('should not transform email to lower case without convertEmailToLowercase option on create', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {});
+ const spy = spyOn(dummyStorageAdapter, 'createObject');
+ spy.and.callThrough();
+ await databaseController.create('_User', {
+ email: 'EXAMPLE@EXAMPLE.COM',
+ });
+ expect(spy.calls.all()[0].args[2]).toEqual({
+ email: 'EXAMPLE@EXAMPLE.COM',
+ ...dates,
+ });
+ });
+
+ it('should transform email to lower case with convertEmailToLowercase option on create', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {
+ convertEmailToLowercase: true,
+ });
+ const spy = spyOn(dummyStorageAdapter, 'createObject');
+ spy.and.callThrough();
+ await databaseController.create('_User', {
+ email: 'EXAMPLE@EXAMPLE.COM',
+ });
+ expect(spy.calls.all()[0].args[2]).toEqual({
+ email: 'example@example.com',
+ ...dates,
+ });
+ });
+
+ it('should not transform email to lower case without convertEmailToLowercase option on update', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {});
+ const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
+ spy.and.callThrough();
+ await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' });
+ expect(spy.calls.all()[0].args[3]).toEqual({
+ email: 'EXAMPLE@EXAMPLE.COM',
+ });
+ });
+
+ it('should transform email to lower case with convertEmailToLowercase option on update', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {
+ convertEmailToLowercase: true,
+ });
+ const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
+ spy.and.callThrough();
+ await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' });
+ expect(spy.calls.all()[0].args[3]).toEqual({
+ email: 'example@example.com',
+ });
+ });
+
+ it('should not find a case insensitive user by email with convertEmailToLowercase', async () => {
+ await reconfigureServer({ convertEmailToLowercase: true });
+ const user = new Parse.User();
+ await user.save({ username: 'EXAMPLE', email: 'EXAMPLE@EXAMPLE.COM', password: 'password' });
+
+ const query = new Parse.Query(Parse.User);
+ query.equalTo('email', 'EXAMPLE@EXAMPLE.COM');
+ const result = await query.find({ useMasterKey: true });
+ expect(result.length).toEqual(0);
+
+ const query2 = new Parse.Query(Parse.User);
+ query2.equalTo('email', 'example@example.com');
+ const result2 = await query2.find({ useMasterKey: true });
+ expect(result2.length).toEqual(1);
+ });
+ });
+
+ describe('convertUsernameToLowercase', () => {
+ const dummyStorageAdapter = {
+ createObject: () => Promise.resolve({ ops: [{}] }),
+ findOneAndUpdate: () => Promise.resolve({}),
+ watch: () => Promise.resolve(),
+ getAllClasses: () =>
+ Promise.resolve([
+ {
+ className: '_User',
+ fields: { username: 'String' },
+ indexes: {},
+ classLevelPermissions: { protectedFields: {} },
+ },
+ ]),
+ };
+ const dates = {
+ createdAt: { iso: undefined, __type: 'Date' },
+ updatedAt: { iso: undefined, __type: 'Date' },
+ };
+
+ it('should not transform username to lower case without convertUsernameToLowercase option on create', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {});
+ const spy = spyOn(dummyStorageAdapter, 'createObject');
+ spy.and.callThrough();
+ await databaseController.create('_User', {
+ username: 'EXAMPLE',
+ });
+ expect(spy.calls.all()[0].args[2]).toEqual({
+ username: 'EXAMPLE',
+ ...dates,
+ });
+ });
+
+ it('should transform username to lower case with convertUsernameToLowercase option on create', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {
+ convertUsernameToLowercase: true,
+ });
+ const spy = spyOn(dummyStorageAdapter, 'createObject');
+ spy.and.callThrough();
+ await databaseController.create('_User', {
+ username: 'EXAMPLE',
+ });
+ expect(spy.calls.all()[0].args[2]).toEqual({
+ username: 'example',
+ ...dates,
+ });
+ });
+
+ it('should not transform username to lower case without convertUsernameToLowercase option on update', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {});
+ const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
+ spy.and.callThrough();
+ await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' });
+ expect(spy.calls.all()[0].args[3]).toEqual({
+ username: 'EXAMPLE',
+ });
+ });
+
+ it('should transform username to lower case with convertUsernameToLowercase option on update', async () => {
+ const databaseController = new DatabaseController(dummyStorageAdapter, {
+ convertUsernameToLowercase: true,
+ });
+ const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
+ spy.and.callThrough();
+ await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' });
+ expect(spy.calls.all()[0].args[3]).toEqual({
+ username: 'example',
+ });
+ });
+
+ it('should not find a case insensitive user by username with convertUsernameToLowercase', async () => {
+ await reconfigureServer({ convertUsernameToLowercase: true });
+ const user = new Parse.User();
+ await user.save({ username: 'EXAMPLE', password: 'password' });
+
+ const query = new Parse.Query(Parse.User);
+ query.equalTo('username', 'EXAMPLE');
+ const result = await query.find({ useMasterKey: true });
+ expect(result.length).toEqual(0);
+
+ const query2 = new Parse.Query(Parse.User);
+ query2.equalTo('username', 'example');
+ const result2 = await query2.find({ useMasterKey: true });
+ expect(result2.length).toEqual(1);
+ });
+ });
+
+ describe('update with validateOnly', () => {
+ const mockStorageAdapter = {
+ findOneAndUpdate: () => Promise.resolve({}),
+ find: () => Promise.resolve([{ objectId: 'test123', testField: 'initialValue' }]),
+ watch: () => Promise.resolve(),
+ getAllClasses: () =>
+ Promise.resolve([
+ {
+ className: 'TestObject',
+ fields: { testField: 'String' },
+ indexes: {},
+ classLevelPermissions: { protectedFields: {} },
+ },
+ ]),
+ };
+
+ it('should use primary readPreference when validateOnly is true', async () => {
+ const databaseController = new DatabaseController(mockStorageAdapter, {});
+ const findSpy = spyOn(mockStorageAdapter, 'find').and.callThrough();
+ const findOneAndUpdateSpy = spyOn(mockStorageAdapter, 'findOneAndUpdate').and.callThrough();
+
+ try {
+ // Call update with validateOnly: true (same as RestWrite.runBeforeSaveTrigger)
+ await databaseController.update(
+ 'TestObject',
+ { objectId: 'test123' },
+ { testField: 'newValue' },
+ {},
+ true, // skipSanitization: true (matches RestWrite behavior)
+ true // validateOnly: true
+ );
+ } catch (error) {
+ // validateOnly may throw, but we're checking the find call options
+ }
+
+ // Verify that find was called with primary readPreference
+ expect(findSpy).toHaveBeenCalled();
+ const findCall = findSpy.calls.mostRecent();
+ expect(findCall.args[3]).toEqual({ readPreference: 'primary' }); // options parameter
+
+ // Verify that findOneAndUpdate was NOT called (only validation, no actual update)
+ expect(findOneAndUpdateSpy).not.toHaveBeenCalled();
+ });
+
+ it('should not use primary readPreference when validateOnly is false', async () => {
+ const databaseController = new DatabaseController(mockStorageAdapter, {});
+ const findSpy = spyOn(mockStorageAdapter, 'find').and.callThrough();
+ const findOneAndUpdateSpy = spyOn(mockStorageAdapter, 'findOneAndUpdate').and.callThrough();
+
+ try {
+ // Call update with validateOnly: false
+ await databaseController.update(
+ 'TestObject',
+ { objectId: 'test123' },
+ { testField: 'newValue' },
+ {},
+ false, // skipSanitization
+ false // validateOnly
+ );
+ } catch (error) {
+ // May throw for other reasons, but we're checking the call pattern
+ }
+
+ // When validateOnly is false, find should not be called for validation
+ // Instead, findOneAndUpdate should be called
+ expect(findSpy).not.toHaveBeenCalled();
+ expect(findOneAndUpdateSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe_only_db('mongo')('update with many', () => {
+ it('should return matchedCount and modifiedCount when multiple docs are updated', async () => {
+ const config = Config.get(Parse.applicationId);
+ const obj1 = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject');
+ const obj3 = new Parse.Object('TestObject');
+ obj1.set('status', 'pending');
+ obj2.set('status', 'pending');
+ obj3.set('status', 'pending');
+ await Parse.Object.saveAll([obj1, obj2, obj3]);
+
+ const result = await config.database.update(
+ 'TestObject',
+ { status: 'pending' },
+ { status: 'done' },
+ { many: true }
+ );
+
+ expect(result.matchedCount).toBe(3);
+ expect(result.modifiedCount).toBe(3);
+ });
+
+ it('should return matchedCount > 0 and modifiedCount 0 when values are already current', async () => {
+ const config = Config.get(Parse.applicationId);
+ const obj1 = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject');
+ obj1.set('status', 'done');
+ obj2.set('status', 'done');
+ await Parse.Object.saveAll([obj1, obj2]);
+
+ const result = await config.database.update(
+ 'TestObject',
+ { status: 'done' },
+ { status: 'done' },
+ { many: true }
+ );
+
+ expect(result.matchedCount).toBe(2);
+ expect(result.modifiedCount).toBe(0);
+ });
+
+ it('should return matchedCount 0 and modifiedCount 0 when no docs match', async () => {
+ const config = Config.get(Parse.applicationId);
+ const result = await config.database.update(
+ 'TestObject',
+ { status: 'nonexistent' },
+ { status: 'done' },
+ { many: true }
+ );
+
+ expect(result.matchedCount).toBe(0);
+ expect(result.modifiedCount).toBe(0);
+ });
+
+ it('should return only matchedCount and modifiedCount for op-based updates', async () => {
+ const config = Config.get(Parse.applicationId);
+ const obj1 = new Parse.Object('TestObject');
+ const obj2 = new Parse.Object('TestObject');
+ obj1.set('score', 1);
+ obj2.set('score', 1);
+ await Parse.Object.saveAll([obj1, obj2]);
+
+ const result = await config.database.update(
+ 'TestObject',
+ { score: { $exists: true } },
+ { score: { __op: 'Increment', amount: 5 } },
+ { many: true }
+ );
+
+ expect(result.matchedCount).toBe(2);
+ expect(result.modifiedCount).toBe(2);
+ expect(Object.keys(result)).toEqual(['matchedCount', 'modifiedCount']);
+ });
+
+ it('should return raw adapter result when skipSanitization is true', async () => {
+ const config = Config.get(Parse.applicationId);
+ const obj1 = new Parse.Object('TestObject');
+ obj1.set('status', 'pending');
+ await obj1.save();
+
+ const result = await config.database.update(
+ 'TestObject',
+ { status: 'pending' },
+ { status: 'done' },
+ { many: true },
+ true // skipSanitization
+ );
+
+ // skipSanitization returns raw adapter result, which for MongoDB
+ // includes additional fields beyond matchedCount and modifiedCount
+ expect(result.matchedCount).toBe(1);
+ expect(result.modifiedCount).toBe(1);
+ expect(result.acknowledged).toBe(true);
+ });
+ });
+});
+
+function buildCLP(pointerNames) {
+ const OPERATIONS = ['count', 'find', 'get', 'create', 'update', 'delete', 'addField'];
+
+ const clp = OPERATIONS.reduce((acc, op) => {
+ acc[op] = {};
+
+ if (pointerNames && pointerNames.length) {
+ acc[op].pointerFields = pointerNames;
+ }
+
+ return acc;
+ }, {});
+
+ clp.protectedFields = {};
+ clp.writeUserFields = [];
+ clp.readUserFields = [];
+
+ return clp;
+}
+
+function createUserPointer(userId) {
+ return {
+ __type: 'Pointer',
+ className: '_User',
+ objectId: userId,
+ };
+}
diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js
new file mode 100644
index 0000000000..b2cae864c1
--- /dev/null
+++ b/spec/DefinedSchemas.spec.js
@@ -0,0 +1,757 @@
+const { DefinedSchemas } = require('../lib/SchemaMigrations/DefinedSchemas');
+const Config = require('../lib/Config');
+
+const cleanUpIndexes = schema => {
+ if (schema.indexes) {
+ delete schema.indexes._id_;
+ if (!Object.keys(schema.indexes).length) {
+ delete schema.indexes;
+ }
+ }
+};
+
+describe('DefinedSchemas', () => {
+ let config;
+ afterEach(async () => {
+ config = Config.get('test');
+ if (config) {
+ await config.database.adapter.deleteAllClasses();
+ }
+ });
+
+ describe('Fields', () => {
+ it('should keep default fields if not provided', async () => {
+ const server = await reconfigureServer();
+ // Will perform create
+ await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ const expectedFields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ };
+ expect(schema.fields).toEqual(expectedFields);
+
+ await server.config.schemaCache.clear();
+ // Will perform update
+ await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual(expectedFields);
+ });
+ it('should protect default fields', async () => {
+ const server = await reconfigureServer();
+
+ const schemas = {
+ definitions: [
+ {
+ className: '_User',
+ fields: {
+ email: 'Object',
+ },
+ },
+ {
+ className: '_Role',
+ fields: {
+ users: 'Object',
+ },
+ },
+ {
+ className: '_Installation',
+ fields: {
+ installationId: 'Object',
+ },
+ },
+ {
+ className: 'Test',
+ fields: {
+ createdAt: { type: 'Object' },
+ objectId: { type: 'Number' },
+ updatedAt: { type: 'String' },
+ ACL: { type: 'String' },
+ },
+ },
+ ],
+ };
+
+ const expectedFields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ };
+
+ const expectedUserFields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ username: { type: 'String' },
+ password: { type: 'String' },
+ email: { type: 'String' },
+ emailVerified: { type: 'Boolean' },
+ authData: { type: 'Object' },
+ };
+
+ const expectedRoleFields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ name: { type: 'String' },
+ users: { type: 'Relation', targetClass: '_User' },
+ roles: { type: 'Relation', targetClass: '_Role' },
+ };
+
+ const expectedInstallationFields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ installationId: { type: 'String' },
+ deviceToken: { type: 'String' },
+ channels: { type: 'Array' },
+ deviceType: { type: 'String' },
+ pushType: { type: 'String' },
+ GCMSenderId: { type: 'String' },
+ timeZone: { type: 'String' },
+ localeIdentifier: { type: 'String' },
+ badge: { type: 'Number' },
+ appVersion: { type: 'String' },
+ appName: { type: 'String' },
+ appIdentifier: { type: 'String' },
+ parseVersion: { type: 'String' },
+ };
+
+ // Perform create
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual(expectedFields);
+
+ let userSchema = await new Parse.Schema('_User').get();
+ expect(userSchema.fields).toEqual(expectedUserFields);
+
+ let roleSchema = await new Parse.Schema('_Role').get();
+ expect(roleSchema.fields).toEqual(expectedRoleFields);
+
+ let installationSchema = await new Parse.Schema('_Installation').get();
+ expect(installationSchema.fields).toEqual(expectedInstallationFields);
+
+ await server.config.schemaCache.clear();
+ // Perform update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual(expectedFields);
+
+ userSchema = await new Parse.Schema('_User').get();
+ expect(userSchema.fields).toEqual(expectedUserFields);
+
+ roleSchema = await new Parse.Schema('_Role').get();
+ expect(roleSchema.fields).toEqual(expectedRoleFields);
+
+ installationSchema = await new Parse.Schema('_Installation').get();
+ expect(installationSchema.fields).toEqual(expectedInstallationFields);
+ });
+ it('should create new fields', async () => {
+ const server = await reconfigureServer();
+ const fields = {
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ aString: { type: 'String' },
+ aStringWithDefault: { type: 'String', defaultValue: 'Test' },
+ aStringWithRequired: { type: 'String', required: true },
+ aStringWithRequiredAndDefault: { type: 'String', required: true, defaultValue: 'Test' },
+ aBoolean: { type: 'Boolean' },
+ aFile: { type: 'File' },
+ aNumber: { type: 'Number' },
+ aRelation: { type: 'Relation', targetClass: '_User' },
+ aPointer: { type: 'Pointer', targetClass: '_Role' },
+ aDate: { type: 'Date' },
+ aGeoPoint: { type: 'GeoPoint' },
+ aPolygon: { type: 'Polygon' },
+ aArray: { type: 'Array' },
+ aObject: { type: 'Object' },
+ };
+ const schemas = {
+ definitions: [
+ {
+ className: 'Test',
+ fields,
+ },
+ ],
+ };
+
+ // Create
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual(fields);
+
+ fields.anotherObject = { type: 'Object' };
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual(fields);
+ });
+ it('should not delete removed fields when "deleteExtraFields" is false', async () => {
+ const server = await reconfigureServer();
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] },
+ server.config
+ ).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toBeDefined();
+
+ await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute();
+
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual({
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ aField: { type: 'String' },
+ ACL: { type: 'ACL' },
+ });
+ });
+ it('should delete removed fields when "deleteExtraFields" is true', async () => {
+ const server = await reconfigureServer();
+
+ await new DefinedSchemas(
+ {
+ definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }],
+ },
+ server.config
+ ).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toBeDefined();
+
+ await new DefinedSchemas(
+ { deleteExtraFields: true, definitions: [{ className: 'Test' }] },
+ server.config
+ ).execute();
+
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields).toEqual({
+ objectId: { type: 'String' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ ACL: { type: 'ACL' },
+ });
+ });
+ it('should re create fields with changed type when "recreateModifiedFields" is true', async () => {
+ const server = await reconfigureServer();
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] },
+ server.config
+ ).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'String' });
+
+ const object = new Parse.Object('Test');
+ await object.save({ aField: 'Hello' }, { useMasterKey: true });
+
+ await new DefinedSchemas(
+ {
+ recreateModifiedFields: true,
+ definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }],
+ },
+ server.config
+ ).execute();
+
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'Number' });
+
+ await object.fetch({ useMasterKey: true });
+ expect(object.get('aField')).toBeUndefined();
+ });
+ it('should not re create fields with changed type when "recreateModifiedFields" is not true', async () => {
+ const server = await reconfigureServer();
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] },
+ server.config
+ ).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'String' });
+
+ const object = new Parse.Object('Test');
+ await object.save({ aField: 'Hello' }, { useMasterKey: true });
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }] },
+ server.config
+ ).execute();
+
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'String' });
+
+ await object.fetch({ useMasterKey: true });
+ expect(object.get('aField')).toBeDefined();
+ });
+ it('should just update classic fields with changed params', async () => {
+ const server = await reconfigureServer();
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] },
+ server.config
+ ).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'String' });
+
+ const object = new Parse.Object('Test');
+ await object.save({ aField: 'Hello' }, { useMasterKey: true });
+
+ await new DefinedSchemas(
+ {
+ definitions: [
+ { className: 'Test', fields: { aField: { type: 'String', required: true } } },
+ ],
+ },
+ server.config
+ ).execute();
+
+ schema = await new Parse.Schema('Test').get();
+ expect(schema.fields.aField).toEqual({ type: 'String', required: true });
+
+ await object.fetch({ useMasterKey: true });
+ expect(object.get('aField')).toEqual('Hello');
+ });
+ });
+
+ describe('Indexes', () => {
+ it('should create new indexes', async () => {
+ const server = await reconfigureServer();
+
+ const indexes = { complex: { createdAt: 1, updatedAt: 1 } };
+
+ const schemas = {
+ definitions: [{ className: 'Test', fields: { aField: { type: 'String' } }, indexes }],
+ };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ let schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual(indexes);
+
+ indexes.complex2 = { createdAt: 1, aField: 1 };
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual(indexes);
+ });
+ it('should re create changed indexes', async () => {
+ const server = await reconfigureServer();
+
+ let indexes = { complex: { createdAt: 1, updatedAt: 1 } };
+
+ let schemas = { definitions: [{ className: 'Test', indexes }] };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ indexes = { complex: { createdAt: 1 } };
+ schemas = { definitions: [{ className: 'Test', indexes }] };
+
+ // Change indexes
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual(indexes);
+
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual(indexes);
+ });
+
+ it('should delete unknown indexes when keepUnknownIndexes is not set', async () => {
+ const server = await reconfigureServer();
+
+ let indexes = { complex: { createdAt: 1, updatedAt: 1 } };
+
+ let schemas = { definitions: [{ className: 'Test', indexes }] };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ indexes = {};
+ schemas = { definitions: [{ className: 'Test', indexes }] };
+ // Change indexes
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toBeUndefined();
+
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toBeUndefined();
+ });
+
+ it('should delete unknown indexes when keepUnknownIndexes is set to false', async () => {
+ const server = await reconfigureServer();
+
+ let indexes = { complex: { createdAt: 1, updatedAt: 1 } };
+
+ let schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: false };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ indexes = {};
+ schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: false };
+ // Change indexes
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toBeUndefined();
+
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toBeUndefined();
+ });
+
+ it('should not delete unknown indexes when keepUnknownIndexes is set to true', async () => {
+ const server = await reconfigureServer();
+
+ const indexes = { complex: { createdAt: 1, updatedAt: 1 } };
+
+ let schemas = { definitions: [{ className: 'Test', indexes }], keepUnknownIndexes: true };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ schemas = { definitions: [{ className: 'Test', indexes: {} }], keepUnknownIndexes: true };
+
+ // Change indexes
+ await new DefinedSchemas(schemas, server.config).execute();
+ let schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual({ complex: { createdAt: 1, updatedAt: 1 } });
+
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ schema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(schema);
+ expect(schema.indexes).toEqual(indexes);
+ });
+
+ xit('should keep protected indexes', async () => {
+ const server = await reconfigureServer();
+
+ const expectedIndexes = {
+ username_1: { username: 1 },
+ case_insensitive_username: { username: 1 },
+ email_1: { email: 1 },
+ case_insensitive_email: { email: 1 },
+ };
+ const schemas = {
+ definitions: [
+ {
+ className: '_User',
+ indexes: {
+ case_insensitive_username: { password: true },
+ case_insensitive_email: { password: true },
+ },
+ },
+ { className: 'Test' },
+ ],
+ };
+ // Create
+ await new DefinedSchemas(schemas, server.config).execute();
+ let userSchema = await new Parse.Schema('_User').get();
+ let testSchema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(userSchema);
+ cleanUpIndexes(testSchema);
+ expect(testSchema.indexes).toBeUndefined();
+ expect(userSchema.indexes).toEqual(expectedIndexes);
+
+ // Update
+ await new DefinedSchemas(schemas, server.config).execute();
+ userSchema = await new Parse.Schema('_User').get();
+ testSchema = await new Parse.Schema('Test').get();
+ cleanUpIndexes(userSchema);
+ cleanUpIndexes(testSchema);
+ expect(testSchema.indexes).toBeUndefined();
+ expect(userSchema.indexes).toEqual(expectedIndexes);
+ });
+
+ it('should detect protected indexes for _User class', () => {
+ const definedSchema = new DefinedSchemas({}, {});
+ const protectedUserIndexes = ['_id_', 'case_insensitive_email', 'username_1', 'email_1'];
+ protectedUserIndexes.forEach(field => {
+ expect(definedSchema.isProtectedIndex('_User', field)).toEqual(true);
+ });
+ expect(definedSchema.isProtectedIndex('_User', 'test')).toEqual(false);
+ });
+
+ it('should detect protected indexes for _Role class', () => {
+ const definedSchema = new DefinedSchemas({}, {});
+ expect(definedSchema.isProtectedIndex('_Role', 'name_1')).toEqual(true);
+ expect(definedSchema.isProtectedIndex('_Role', 'test')).toEqual(false);
+ });
+
+ it('should detect protected indexes for _Idempotency class', () => {
+ const definedSchema = new DefinedSchemas({}, {});
+ expect(definedSchema.isProtectedIndex('_Idempotency', 'reqId_1')).toEqual(true);
+ expect(definedSchema.isProtectedIndex('_Idempotency', 'test')).toEqual(false);
+ });
+
+ it('should not detect protected indexes on user defined class', () => {
+ const definedSchema = new DefinedSchemas({}, {});
+ const protectedIndexes = [
+ 'case_insensitive_email',
+ 'username_1',
+ 'email_1',
+ 'reqId_1',
+ 'name_1',
+ ];
+ protectedIndexes.forEach(field => {
+ expect(definedSchema.isProtectedIndex('ExampleClass', field)).toEqual(false);
+ });
+ expect(definedSchema.isProtectedIndex('ExampleClass', '_id_')).toEqual(true);
+ });
+ });
+
+ describe('ClassLevelPermissions', () => {
+ it('should use default CLP', async () => {
+ const server = await reconfigureServer();
+ const schemas = { definitions: [{ className: 'Test' }] };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ const expectedTestCLP = {
+ find: {},
+ count: {},
+ get: {},
+ create: {},
+ update: {},
+ delete: {},
+ addField: {},
+ protectedFields: {},
+ };
+ let testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+
+ await new DefinedSchemas(schemas, server.config).execute();
+ testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+ });
+ it('should save CLP', async () => {
+ const server = await reconfigureServer();
+
+ const expectedTestCLP = {
+ find: {},
+ count: { requiresAuthentication: true },
+ get: { 'role:Admin': true },
+ create: { 'role:ARole': true, requiresAuthentication: true },
+ update: { requiresAuthentication: true },
+ delete: { requiresAuthentication: true },
+ addField: {},
+ protectedFields: { '*': ['aField'], 'role:Admin': ['anotherField'] },
+ };
+ const schemas = {
+ definitions: [
+ {
+ className: 'Test',
+ fields: { aField: { type: 'String' }, anotherField: { type: 'Object' } },
+ classLevelPermissions: expectedTestCLP,
+ },
+ ],
+ };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ let testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+
+ expectedTestCLP.update = {};
+ expectedTestCLP.create = { requiresAuthentication: true };
+
+ await new DefinedSchemas(schemas, server.config).execute();
+ testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+ });
+ it('should force addField to empty', async () => {
+ const server = await reconfigureServer();
+ const schemas = {
+ definitions: [{ className: 'Test', classLevelPermissions: { addField: { '*': true } } }],
+ };
+ await new DefinedSchemas(schemas, server.config).execute();
+
+ const expectedTestCLP = {
+ find: {},
+ count: {},
+ get: {},
+ create: {},
+ update: {},
+ delete: {},
+ addField: {},
+ protectedFields: {},
+ };
+
+ let testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+
+ await new DefinedSchemas(schemas, server.config).execute();
+ testSchema = await new Parse.Schema('Test').get();
+ expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP);
+ });
+ });
+
+ it('should not delete classes automatically', async () => {
+ await reconfigureServer({
+ schema: { definitions: [{ className: '_User' }, { className: 'Test' }] },
+ });
+
+ await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } });
+
+ const schema = await new Parse.Schema('Test').get();
+ expect(schema.className).toEqual('Test');
+ });
+
+ it('should disable class PUT/POST endpoint when lockSchemas provided to avoid dual source of truth', async () => {
+ await reconfigureServer({
+ schema: {
+ lockSchemas: true,
+ definitions: [{ className: '_User' }, { className: 'Test' }],
+ },
+ });
+
+ const schema = await new Parse.Schema('Test').get();
+ expect(schema.className).toEqual('Test');
+
+ const schemas = await Parse.Schema.all();
+ // Role could be flaky since all system classes are not ensured
+ // at start up by the DefinedSchema system
+ expect(schemas.filter(({ className }) => className !== '_Role').length).toEqual(3);
+
+ await expectAsync(new Parse.Schema('TheNewTest').save()).toBeRejectedWithError(
+ 'Cannot perform this operation when schemas options is used.'
+ );
+
+ await expectAsync(new Parse.Schema('_User').update()).toBeRejectedWithError(
+ 'Cannot perform this operation when schemas options is used.'
+ );
+ });
+ it('should only enable delete class endpoint since', async () => {
+ await reconfigureServer({
+ schema: { definitions: [{ className: '_User' }, { className: 'Test' }] },
+ });
+ await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } });
+
+ let schemas = await Parse.Schema.all();
+ expect(schemas.length).toEqual(4);
+
+ await new Parse.Schema('_User').delete();
+ schemas = await Parse.Schema.all();
+ expect(schemas.length).toEqual(3);
+ });
+ it('should run beforeMigration before execution of DefinedSchemas', async () => {
+ const config = {
+ schema: {
+ definitions: [{ className: '_User' }, { className: 'Test' }],
+ beforeMigration: async () => {},
+ },
+ };
+ const spy = spyOn(config.schema, 'beforeMigration');
+ await reconfigureServer(config);
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+ it('should run afterMigration after execution of DefinedSchemas', async () => {
+ const config = {
+ schema: {
+ definitions: [{ className: '_User' }, { className: 'Test' }],
+ afterMigration: async () => {},
+ },
+ };
+ const spy = spyOn(config.schema, 'afterMigration');
+ await reconfigureServer(config);
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it('should use logger in case of error', async () => {
+ const server = await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } });
+ const error = new Error('A test error');
+ const logger = require('../lib/logger').logger;
+ spyOn(DefinedSchemas.prototype, 'wait').and.resolveTo();
+ spyOn(logger, 'error').and.callThrough();
+ spyOn(DefinedSchemas.prototype, 'createDeleteSession').and.callFake(() => {
+ throw error;
+ });
+
+ await new DefinedSchemas(
+ { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] },
+ server.config
+ ).execute();
+
+ expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`);
+ });
+
+ it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)('should perform migration in parallel without failing', async () => {
+ const server = await reconfigureServer();
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callThrough();
+ const migrationOptions = {
+ definitions: [
+ {
+ className: 'Test',
+ fields: { aField: { type: 'String' } },
+ indexes: { aField: { aField: 1 } },
+ classLevelPermissions: {
+ create: { requiresAuthentication: true },
+ },
+ },
+ ],
+ };
+
+ // Simulate parallel deployment
+ await Promise.all([
+ new DefinedSchemas(migrationOptions, server.config).execute(),
+ new DefinedSchemas(migrationOptions, server.config).execute(),
+ new DefinedSchemas(migrationOptions, server.config).execute(),
+ new DefinedSchemas(migrationOptions, server.config).execute(),
+ new DefinedSchemas(migrationOptions, server.config).execute(),
+ ]);
+
+ const testSchema = (await Parse.Schema.all()).find(
+ ({ className }) => className === migrationOptions.definitions[0].className
+ );
+
+ expect(testSchema.indexes.aField).toEqual({ aField: 1 });
+ expect(testSchema.fields.aField).toEqual({ type: 'String' });
+ expect(testSchema.classLevelPermissions.create).toEqual({ requiresAuthentication: true });
+ expect(logger.error).toHaveBeenCalledTimes(0);
+ });
+
+ it('should not affect cacheAdapter', async () => {
+ const server = await reconfigureServer();
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callThrough();
+ const migrationOptions = {
+ definitions: [
+ {
+ className: 'Test',
+ fields: { aField: { type: 'String' } },
+ indexes: { aField: { aField: 1 } },
+ classLevelPermissions: {
+ create: { requiresAuthentication: true },
+ },
+ },
+ ],
+ };
+
+ const cacheAdapter = {
+ get: () => Promise.resolve(null),
+ put: () => {},
+ del: () => {},
+ clear: () => {},
+ connect: jasmine.createSpy('clear'),
+ };
+ server.config.cacheAdapter = cacheAdapter;
+ await new DefinedSchemas(migrationOptions, server.config).execute();
+ expect(cacheAdapter.connect).not.toHaveBeenCalled();
+ });
+});
diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js
new file mode 100644
index 0000000000..993d18682f
--- /dev/null
+++ b/spec/Deprecator.spec.js
@@ -0,0 +1,272 @@
+'use strict';
+
+const Deprecator = require('../lib/Deprecator/Deprecator');
+
+describe('Deprecator', () => {
+ let deprecations = [];
+
+ beforeEach(async () => {
+ deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
+ });
+
+ it('deprecations are an array', async () => {
+ expect(Deprecator._getDeprecations()).toBeInstanceOf(Array);
+ });
+
+ it('logs deprecation for new default', async () => {
+ deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy.calls.all()[0].args[0]).toEqual(
+ `DeprecationWarning: The Parse Server option '${deprecations[0].optionKey}' default will change to '${deprecations[0].changeNewDefault}' in a future version.`
+ );
+ });
+
+ it('does not log deprecation for new default if option is set manually', async () => {
+ deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+ await reconfigureServer({ [deprecations[0].optionKey]: 'manuallySet' });
+ expect(logSpy).not.toHaveBeenCalled();
+ });
+
+ it('logs runtime deprecation', async () => {
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
+ const options = { usage: 'Doing this', solution: 'Do that instead.' };
+
+ Deprecator.logRuntimeDeprecation(options);
+ expect(logSpy.calls.all()[0].args[0]).toEqual(
+ `DeprecationWarning: ${options.usage} is deprecated and will be removed in a future version. ${options.solution}`
+ );
+ });
+
+ it('logs deprecation for nested option key with dot notation', async () => {
+ deprecations = [{ optionKey: 'databaseOptions.testOption', changeNewDefault: 'false' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy.calls.all()[0].args[0]).toEqual(
+ `DeprecationWarning: The Parse Server option '${deprecations[0].optionKey}' default will change to '${deprecations[0].changeNewDefault}' in a future version.`
+ );
+ });
+
+ it('does not log deprecation for nested option key if option is set manually', async () => {
+ deprecations = [{ optionKey: 'databaseOptions.testOption', changeNewDefault: 'false' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+ const Config = require('../lib/Config');
+ const config = Config.get('test');
+ // Directly test scanParseServerOptions with nested option set
+ Deprecator.scanParseServerOptions({ databaseOptions: { testOption: true } });
+ expect(logSpy).not.toHaveBeenCalled();
+ });
+
+ it('logs deprecation for allowedFileUrlDomains when not set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ // Pass a fresh fileUpload object without allowedFileUrlDomains to avoid
+ // inheriting the mutated default from a previous reconfigureServer() call.
+ await reconfigureServer({
+ fileUpload: {
+ enableForPublic: true,
+ enableForAnonymousUser: true,
+ enableForAuthenticatedUser: true,
+ },
+ });
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'fileUpload.allowedFileUrlDomains',
+ changeNewDefault: '[]',
+ })
+ );
+ });
+
+ it('does not log deprecation for allowedFileUrlDomains when explicitly set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({
+ fileUpload: { allowedFileUrlDomains: ['*'] },
+ });
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'fileUpload.allowedFileUrlDomains',
+ })
+ );
+ });
+
+ it('logs deprecation for removed key when option is set', async () => {
+ deprecations = [{ optionKey: 'exampleKey', changeNewKey: '', solution: 'Use something else.' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
+
+ await reconfigureServer({ exampleKey: true });
+ expect(logSpy).toHaveBeenCalledWith(
+ `DeprecationWarning: The Parse Server option '${deprecations[0].optionKey}' is deprecated and will be removed in a future version. ${deprecations[0].solution}`
+ );
+ });
+
+ it('does not log deprecation for removed key when option is not set', async () => {
+ deprecations = [{ optionKey: 'exampleKey', changeNewKey: '', solution: 'Use something else.' }];
+
+ spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy).not.toHaveBeenCalled();
+ });
+
+ it('logs deprecation for mountPlayground when set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({ mountPlayground: true, mountGraphQL: true });
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'mountPlayground',
+ changeNewKey: '',
+ })
+ );
+ });
+
+ it('does not log deprecation for mountPlayground when not set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'mountPlayground',
+ })
+ );
+ });
+
+ it('logs deprecation for requestComplexity limits when not set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer();
+ const keys = [
+ 'requestComplexity.includeDepth',
+ 'requestComplexity.includeCount',
+ 'requestComplexity.subqueryDepth',
+ 'requestComplexity.queryDepth',
+ 'requestComplexity.graphQLDepth',
+ 'requestComplexity.graphQLFields',
+ ];
+ for (const key of keys) {
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: key,
+ })
+ );
+ }
+ });
+
+ it('logs deprecation for enableProductPurchaseLegacyApi when set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({ enableProductPurchaseLegacyApi: true });
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'enableProductPurchaseLegacyApi',
+ changeNewKey: '',
+ })
+ );
+ });
+
+ it('does not log deprecation for enableProductPurchaseLegacyApi when not set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'enableProductPurchaseLegacyApi',
+ })
+ );
+ });
+
+ it('does not log deprecation for enableProductPurchaseLegacyApi when set to false', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({ enableProductPurchaseLegacyApi: false });
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'enableProductPurchaseLegacyApi',
+ })
+ );
+ });
+
+ it('does not log deprecation for requestComplexity limits when explicitly set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({
+ requestComplexity: {
+ includeDepth: 10,
+ includeCount: 100,
+ subqueryDepth: 10,
+ queryDepth: 10,
+ graphQLDepth: 20,
+ graphQLFields: 200,
+ },
+ });
+ const keys = [
+ 'requestComplexity.includeDepth',
+ 'requestComplexity.includeCount',
+ 'requestComplexity.subqueryDepth',
+ 'requestComplexity.queryDepth',
+ 'requestComplexity.graphQLDepth',
+ 'requestComplexity.graphQLFields',
+ ];
+ for (const key of keys) {
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: key,
+ })
+ );
+ }
+ });
+
+ it('registers a deprecation entry for installation.duplicateDeviceTokenActionEnforceAuth', () => {
+ const Deprecations = require('../lib/Deprecator/Deprecations');
+ const entry = Deprecations.find(
+ d => d.optionKey === 'installation.duplicateDeviceTokenActionEnforceAuth'
+ );
+ expect(entry).toBeDefined();
+ expect(entry.changeNewDefault).toBe('true');
+ expect(entry.solution).toContain('duplicateDeviceTokenActionEnforceAuth');
+ });
+
+ it('logs deprecation for installation.duplicateDeviceTokenActionEnforceAuth when not set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer();
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'installation.duplicateDeviceTokenActionEnforceAuth',
+ changeNewDefault: 'true',
+ })
+ );
+ });
+
+ it('does not log deprecation for installation.duplicateDeviceTokenActionEnforceAuth when explicitly set', async () => {
+ const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
+
+ await reconfigureServer({
+ installation: { duplicateDeviceTokenActionEnforceAuth: false },
+ });
+ expect(logSpy).not.toHaveBeenCalledWith(
+ jasmine.objectContaining({
+ optionKey: 'installation.duplicateDeviceTokenActionEnforceAuth',
+ })
+ );
+ });
+});
diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js
index 93acc2ea82..11a901f399 100644
--- a/spec/EmailVerificationToken.spec.js
+++ b/spec/EmailVerificationToken.spec.js
@@ -1,515 +1,1303 @@
-"use strict";
+'use strict';
-const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
-const request = require('request');
-const MongoClient = require("mongodb").MongoClient;
+const Auth = require('../lib/Auth');
+const Config = require('../lib/Config');
+const request = require('../lib/request');
+const { resolvingPromise, sleep } = require('../lib/TestUtils');
+const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
-describe("Email Verification Token Expiration: ", () => {
-
- it_exclude_dbs(['postgres'])('show the invalid link page, if the user clicks on the verify email link after the email verify token expires', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+describe('Email Verification Token Expiration:', () => {
+ it('show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 0.5, // 0.5 second
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- }).then(() => {
- // wait for 1 second - simulate user behavior to some extent
- setTimeout(() => {
- expect(sendEmailOptions).not.toBeUndefined();
-
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
- done();
- });
- }, 1000);
+ publicServerURL: 'http://localhost:8378/1',
});
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ // wait for 1 second - simulate user behavior to some extent
+ await sleep(1000);
+
+ expect(sendEmailOptions).not.toBeUndefined();
+
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('Invalid verification link!');
});
- it_exclude_dbs(['postgres'])('emailVerified should set to false, if the user does not verify their email before the email verify token expires', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it('emailVerified should set to false, if the user does not verify their email before the email verify token expires', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 0.5, // 0.5 second
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- }).then(() => {
- // wait for 1 second - simulate user behavior to some extent
- setTimeout(() => {
- expect(sendEmailOptions).not.toBeUndefined();
-
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- user.fetch()
- .then(() => {
- expect(user.get('emailVerified')).toEqual(false);
- done();
- })
- .catch((err) => {
- fail("this should not fail");
- done();
- });
- });
- }, 1000);
+ publicServerURL: 'http://localhost:8378/1',
});
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ // wait for 1 second - simulate user behavior to some extent
+ await sleep(1000);
+
+ expect(sendEmailOptions).not.toBeUndefined();
+
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ await user.fetch();
+ expect(user.get('emailVerified')).toEqual(false);
});
- it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)('if user clicks on the email verify link before email verification token expiration then show the verify email success page', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- }).then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=testEmailVerifyTokenValidity');
- done();
- });
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
});
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('Email verified!');
});
- it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- }).then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- user.fetch()
- .then(() => {
- expect(user.get('emailVerified')).toEqual(true);
- done();
- })
- .catch((err) => {
- fail("this should not fail");
- done();
- });
- });
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
});
+ expect(response.status).toEqual(200);
+ await user.fetch();
+ expect(user.get('emailVerified')).toEqual(true);
});
- it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)('if user clicks on the email verify link before email verification token expiration then user should be able to login', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- }).then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- Parse.User.logIn("testEmailVerifyTokenValidity", "expiringToken")
- .then(user => {
- expect(typeof user).toBe('object');
- expect(user.get('emailVerified')).toBe(true);
- done();
- })
- .catch((error) => {
- fail('login should have succeeded');
- done();
- });
- });
+ publicServerURL: 'http://localhost:8378/1',
});
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ const verifiedUser = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken');
+ expect(typeof verifiedUser).toBe('object');
+ expect(verifiedUser.get('emailVerified')).toBe(true);
});
- it_exclude_dbs(['postgres'])('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: 'http://localhost:8378/1'
- })
- .then(() => {
- user.setUsername('sets_email_verify_token_expires_at');
- user.setPassword('expiringToken');
- user.set('email', 'user@parse.com');
- return user.signUp();
- })
- .then(() => {
- const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
- return MongoClient.connect(databaseURI);
- })
- .then(database => {
- expect(typeof database).toBe('object');
- return database.collection('test__User').findOne({username: 'sets_email_verify_token_expires_at'});
- })
- .then(user => {
- expect(typeof user).toBe('object');
- expect(user.emailVerified).toEqual(false);
- expect(typeof user._email_verify_token).toBe('string');
- expect(typeof user._email_verify_token_expires_at).toBe('object');
- done();
- })
- .catch(error => {
- fail("this should not fail");
- done();
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('sets_email_verify_token_expires_at');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const config = Config.get('test');
+ const results = await config.database.find(
+ '_User',
+ {
+ username: 'sets_email_verify_token_expires_at',
+ },
+ {},
+ Auth.maintenance(config)
+ );
+ expect(results.length).toBe(1);
+ const verifiedUser = results[0];
+ expect(typeof verifiedUser).toBe('object');
+ expect(verifiedUser.emailVerified).toEqual(false);
+ expect(typeof verifiedUser._email_verify_token).toBe('string');
+ expect(typeof verifiedUser._email_verify_token_expires_at).toBe('object');
+ expect(sendEmailOptions).toBeDefined();
+ });
+
+ it('can resend email using an expired token', async () => {
+ const user = new Parse.User();
+ const emailAdapter = {
+ sendVerificationEmail: () => {},
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('test');
+ user.setPassword('password');
+ user.set('email', 'user@example.com');
+ await user.signUp();
+
+ await Parse.Server.database.update(
+ '_User',
+ { objectId: user.id },
+ {
+ _email_verify_token_expires_at: Parse._encode(new Date('2000')),
+ }
+ );
+
+ const obj = await Parse.Server.database.find(
+ '_User',
+ { objectId: user.id },
+ {},
+ Auth.maintenance(Parse.Server)
+ );
+ const token = obj[0]._email_verify_token;
+
+ const res = await request({
+ url: `http://localhost:8378/1/apps/test/verify_email?token=${token}`,
+ method: 'GET',
});
+ expect(res.text).toContain('Invalid verification link!');
+
+ const formUrl = `http://localhost:8378/1/apps/test/resend_verification_email`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ token: token,
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.text).toContain('email_verification_send_success.html');
});
- it_exclude_dbs(['postgres'])('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => {
+ let sendEmailOptions;
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ const verifyUserEmails = {
+ method(req) {
+ expect(Object.keys(req)).toEqual([
+ 'original',
+ 'object',
+ 'master',
+ 'ip',
+ 'installationId',
+ 'createdWith',
+ ]);
+ expect(req.createdWith).toEqual({ action: 'signup', authProvider: 'password' });
+ return false;
+ },
+ };
+ const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: verifyUserEmails.method,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ const beforeSave = {
+ method(req) {
+ req.object.set('emailVerified', true);
+ },
+ };
+ const saveSpy = spyOn(beforeSave, 'method').and.callThrough();
+ const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
+ Parse.Cloud.beforeSave(Parse.User, beforeSave.method);
+ const user = new Parse.User();
+ user.setUsername('sets_email_verify_token_expires_at');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@example.com');
+ await user.signUp();
+
+ const config = Config.get('test');
+ const results = await config.database.find(
+ '_User',
+ {
+ username: 'sets_email_verify_token_expires_at',
+ },
+ {},
+ Auth.maintenance(config)
+ );
+
+ expect(results.length).toBe(1);
+ const user_data = results[0];
+ expect(typeof user_data).toBe('object');
+ expect(user_data.emailVerified).toEqual(true);
+ expect(user_data._email_verify_token).toBeUndefined();
+ expect(user_data._email_verify_token_expires_at).toBeUndefined();
+ expect(emailSpy).not.toHaveBeenCalled();
+ expect(saveSpy).toHaveBeenCalled();
+ expect(sendEmailOptions).toBeUndefined();
+ expect(verifySpy).toHaveBeenCalled();
+ });
+
+ it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)('can conditionally send emails and allow conditional login', async () => {
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ const verifyUserEmails = {
+ method(req) {
+ expect(Object.keys(req)).toEqual([
+ 'original',
+ 'object',
+ 'master',
+ 'ip',
+ 'installationId',
+ 'createdWith',
+ ]);
+ expect(req.createdWith).toEqual({ action: 'signup', authProvider: 'password' });
+ if (req.object.get('username') === 'no_email') {
+ return false;
+ }
+ return true;
+ },
+ };
+ const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: verifyUserEmails.method,
+ preventLoginWithUnverifiedEmail: verifyUserEmails.method,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ const user = new Parse.User();
+ user.setUsername('no_email');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@example.com');
+ await user.signUp();
+ expect(sendEmailOptions).toBeUndefined();
+ expect(user.getSessionToken()).toBeDefined();
+ expect(verifySpy).toHaveBeenCalledTimes(2);
+ const user2 = new Parse.User();
+ user2.setUsername('email');
+ user2.setPassword('expiringToken');
+ user2.set('email', 'user2@example.com');
+ await user2.signUp();
+ await sendPromise;
+ expect(user2.getSessionToken()).toBeUndefined();
+ expect(sendEmailOptions).toBeDefined();
+ expect(verifySpy).toHaveBeenCalledTimes(5);
+ });
+
+ it('provides createdWith on signup when verification blocks session creation', async () => {
+ const verifyUserEmails = {
+ method: params => {
+ expect(params.object).toBeInstanceOf(Parse.User);
+ expect(params.createdWith).toEqual({ action: 'signup', authProvider: 'password' });
+ return true;
+ },
+ };
+ const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: verifyUserEmails.method,
+ preventLoginWithUnverifiedEmail: true,
+ preventSignupWithUnverifiedEmail: true,
+ emailAdapter: MockEmailAdapterWithOptions({
+ fromAddress: 'parse@example.com',
+ apiKey: 'k',
+ domain: 'd',
+ }),
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ const user = new Parse.User();
+ user.setUsername('signup_created_with');
+ user.setPassword('pass');
+ user.setEmail('signup@example.com');
+ const res = await user.signUp().catch(e => e);
+ expect(res.message).toBe('User email is not verified.');
+ expect(user.getSessionToken()).toBeUndefined();
+ expect(verifySpy).toHaveBeenCalledTimes(2); // before signup completion and on preventLoginWithUnverifiedEmail
+ });
+
+ it('provides createdWith with auth provider on login verification', async () => {
+ const user = new Parse.User();
+ user.setUsername('user_created_with_login');
+ user.setPassword('pass');
+ user.set('email', 'login@example.com');
+ await user.signUp();
+
+ const verifyUserEmails = {
+ method: async params => {
+ expect(params.object).toBeInstanceOf(Parse.User);
+ expect(params.createdWith).toEqual({ action: 'login', authProvider: 'password' });
+ return true;
+ },
+ };
+ const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ publicServerURL: 'http://localhost:8378/1',
+ verifyUserEmails: verifyUserEmails.method,
+ preventLoginWithUnverifiedEmail: verifyUserEmails.method,
+ preventSignupWithUnverifiedEmail: true,
+ emailAdapter: MockEmailAdapterWithOptions({
+ fromAddress: 'parse@example.com',
+ apiKey: 'k',
+ domain: 'd',
+ }),
+ });
+
+ const res = await Parse.User.logIn('user_created_with_login', 'pass').catch(e => e);
+ expect(res.code).toBe(205);
+ expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2); // before login completion and on preventLoginWithUnverifiedEmail
+ });
+
+ it('provides createdWith with auth provider on signup verification', async () => {
+ const createdWithValues = [];
+ const verifyUserEmails = {
+ method: params => {
+ createdWithValues.push(params.createdWith);
+ return true;
+ },
+ };
+ const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: verifyUserEmails.method,
+ preventLoginWithUnverifiedEmail: true,
+ preventSignupWithUnverifiedEmail: true,
+ emailAdapter: MockEmailAdapterWithOptions({
+ fromAddress: 'parse@example.com',
+ apiKey: 'k',
+ domain: 'd',
+ }),
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ const provider = {
+ authData: { id: '8675309', access_token: 'jenny' },
+ shouldError: false,
+ authenticate(options) {
+ options.success(this, this.authData);
+ },
+ restoreAuthentication() {
+ return true;
+ },
+ getAuthType() {
+ return 'facebook';
+ },
+ deauthenticate() {},
+ };
+ Parse.User._registerAuthenticationProvider(provider);
+ const res = await Parse.User._logInWith('facebook').catch(e => e);
+ expect(res.message).toBe('User email is not verified.');
+ // Called once in createSessionTokenIfNeeded (no email set, so _validateEmail skips)
+ expect(verifySpy).toHaveBeenCalledTimes(1);
+ expect(createdWithValues[0]).toEqual({ action: 'signup', authProvider: 'facebook' });
+ });
+
+ it('provides createdWith for preventLoginWithUnverifiedEmail function', async () => {
+ const user = new Parse.User();
+ user.setUsername('user_prevent_login_fn');
+ user.setPassword('pass');
+ user.set('email', 'preventlogin@example.com');
+ await user.signUp();
+
+ const preventLoginCreatedWith = [];
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ publicServerURL: 'http://localhost:8378/1',
+ verifyUserEmails: true,
+ preventLoginWithUnverifiedEmail: params => {
+ preventLoginCreatedWith.push(params.createdWith);
+ return true;
+ },
+ emailAdapter: MockEmailAdapterWithOptions({
+ fromAddress: 'parse@example.com',
+ apiKey: 'k',
+ domain: 'd',
+ }),
+ });
+
+ const res = await Parse.User.logIn('user_prevent_login_fn', 'pass').catch(e => e);
+ expect(res.code).toBe(205);
+ expect(preventLoginCreatedWith.length).toBe(1);
+ expect(preventLoginCreatedWith[0]).toEqual({ action: 'login', authProvider: 'password' });
+ });
+
+ it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)('can conditionally send user email verification', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => {},
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ const sendVerificationEmail = {
+ method(req) {
+ expect(req.user).toBeDefined();
+ expect(req.master).toBeDefined();
+ return false;
+ },
+ };
+ const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough();
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("unsets_email_verify_token_expires_at");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- })
- .then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
-
- const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
- MongoClient.connect(databaseURI)
- .then(database => {
- expect(typeof database).toBe('object');
- return database.collection('test__User').findOne({username: 'unsets_email_verify_token_expires_at'});
- })
- .then(user => {
- expect(typeof user).toBe('object');
- expect(user.emailVerified).toEqual(true);
- expect(typeof user._email_verify_token).toBe('undefined');
- expect(typeof user._email_verify_token_expires_at).toBe('undefined');
- done();
- })
- .catch(error => {
- fail("this should not fail");
- done();
- });
- });
- })
- .catch(error => {
- fail("this should not fail");
- done();
+ publicServerURL: 'http://localhost:8378/1',
+ sendUserEmailVerification: sendVerificationEmail.method,
+ });
+ const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
+ const newUser = new Parse.User();
+ newUser.setUsername('unsets_email_verify_token_expires_at');
+ newUser.setPassword('expiringToken');
+ newUser.set('email', 'user@example.com');
+ await newUser.signUp();
+ await Parse.User.requestEmailVerification('user@example.com');
+ await sleep(100);
+ expect(sendSpy).toHaveBeenCalledTimes(2);
+ expect(emailSpy).toHaveBeenCalledTimes(0);
+ });
+
+ it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)('provides full user object in email verification function on email and username change', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => {},
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ const sendVerificationEmail = {
+ method(req) {
+ expect(req.user).toBeDefined();
+ expect(req.user.id).toBeDefined();
+ expect(req.user.get('createdAt')).toBeDefined();
+ expect(req.user.get('updatedAt')).toBeDefined();
+ expect(req.master).toBeDefined();
+ return false;
+ },
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5,
+ publicServerURL: 'http://localhost:8378/1',
+ sendUserEmailVerification: sendVerificationEmail.method,
});
+ const user = new Parse.User();
+ user.setPassword('password');
+ user.setUsername('new@example.com');
+ user.setEmail('user@example.com');
+ await user.save(null, { useMasterKey: true });
+
+ // Update email and username
+ user.setUsername('new@example.com');
+ user.setEmail('new@example.com');
+ await user.save(null, { useMasterKey: true });
});
- it_exclude_dbs(['postgres'])('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)('beforeSave options do not change existing behaviour', async () => {
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- var serverConfig = {
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough();
+ const newUser = new Parse.User();
+ newUser.setUsername('unsets_email_verify_token_expires_at');
+ newUser.setPassword('expiringToken');
+ newUser.set('email', 'user@parse.com');
+ await newUser.signUp();
+ await sendPromise;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ const config = Config.get('test');
+ const results = await config.database.find('_User', {
+ username: 'unsets_email_verify_token_expires_at',
+ });
+
+ expect(results.length).toBe(1);
+ const user = results[0];
+ expect(typeof user).toBe('object');
+ expect(user.emailVerified).toEqual(true);
+ expect(typeof user._email_verify_token).toBe('undefined');
+ expect(typeof user._email_verify_token_expires_at).toBe('undefined');
+ expect(emailSpy).toHaveBeenCalled();
+ });
+
+ it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('unsets_email_verify_token_expires_at');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ const config = Config.get('test');
+ const results = await config.database.find('_User', {
+ username: 'unsets_email_verify_token_expires_at',
+ });
+ expect(results.length).toBe(1);
+ const verifiedUser = results[0];
+
+ expect(typeof verifiedUser).toBe('object');
+ expect(verifiedUser.emailVerified).toEqual(true);
+ expect(typeof verifiedUser._email_verify_token).toBe('undefined');
+ expect(typeof verifiedUser._email_verify_token_expires_at).toBe('undefined');
+ });
+
+ it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ const serverConfig = {
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
- publicServerURL: "http://localhost:8378/1"
+ publicServerURL: 'http://localhost:8378/1',
};
// setup server WITHOUT enabling the expire email verify token flag
- reconfigureServer(serverConfig)
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- })
- .then(() => {
- return new Promise((resolve, reject) => {
- request.get(sendEmailOptions.link, { followRedirect: false, })
- .on('error', error => reject(error))
- .on('response', (response) => {
- expect(response.statusCode).toEqual(302);
- resolve(user.fetch());
- });
- });
- })
- .then(() => {
- expect(user.get('emailVerified')).toEqual(true);
- // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
- serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
- return reconfigureServer(serverConfig);
- })
- .then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
- done();
- });
- })
- .catch((err) => {
- fail("this should not fail");
- done();
+ await reconfigureServer(serverConfig);
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ let response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
});
+ expect(response.status).toEqual(200);
+ await user.fetch();
+ expect(user.get('emailVerified')).toEqual(true);
+ // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
+ serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
+ await reconfigureServer(serverConfig);
+
+ response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('Invalid verification link!');
});
- it_exclude_dbs(['postgres'])('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- var serverConfig = {
+ sendMail: () => {},
+ };
+ const serverConfig = {
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
- publicServerURL: "http://localhost:8378/1"
+ publicServerURL: 'http://localhost:8378/1',
};
// setup server WITHOUT enabling the expire email verify token flag
- reconfigureServer(serverConfig)
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- })
- .then(() => {
- // just get the user again - DO NOT email verify the user
- return user.fetch();
- })
- .then(() => {
- expect(user.get('emailVerified')).toEqual(false);
- // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
- serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
- return reconfigureServer(serverConfig);
- })
- .then(() => {
- request.get(sendEmailOptions.link, {
- followRedirect: false,
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
- done();
- });
- })
- .catch((err) => {
- fail("this should not fail");
- done();
+ await reconfigureServer(serverConfig);
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ // just get the user again - DO NOT email verify the user
+ await user.fetch();
+
+ expect(user.get('emailVerified')).toEqual(false);
+ // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
+ serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
+ await reconfigureServer(serverConfig);
+
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
});
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('Invalid verification link!');
});
- it_exclude_dbs(['postgres'])('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
+ it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', async () => {
+ const user = new Parse.User();
- const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
- let db;
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ const serverConfig = {
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ };
- let user = new Parse.User();
- let userBeforeEmailReset;
+ await reconfigureServer(serverConfig);
+ user.setUsername('newEmailVerifyTokenOnEmailReset');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ const config = Config.get('test');
+ const userFromDb = await config.database
+ .find('_User', { username: 'newEmailVerifyTokenOnEmailReset' })
+ .then(results => {
+ return results[0];
+ });
+ expect(typeof userFromDb).toBe('object');
+ const userBeforeEmailReset = userFromDb;
+
+ // trigger another token generation by setting the email
+ user.set('email', 'user@parse.com');
+ await new Promise(resolve => {
+ // wait for half a sec to get a new expiration time
+ setTimeout(() => resolve(user.save()), 500);
+ });
+ const userAfterEmailReset = await config.database
+ .find(
+ '_User',
+ { username: 'newEmailVerifyTokenOnEmailReset' },
+ {},
+ Auth.maintenance(config)
+ )
+ .then(results => {
+ return results[0];
+ });
+
+ expect(typeof userAfterEmailReset).toBe('object');
+ expect(userBeforeEmailReset._email_verify_token).not.toEqual(
+ userAfterEmailReset._email_verify_token
+ );
+ expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual(
+ userAfterEmailReset._email_verify_token_expires_at
+ );
+ expect(sendEmailOptions).toBeDefined();
+ });
+ it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)('should send a new verification email when a resend is requested and the user is UNVERIFIED', async () => {
+ const user = new Parse.User();
let sendEmailOptions;
- let emailAdapter = {
+ let sendVerificationEmailCallCount = 0;
+ const promise1 = resolvingPromise();
+ const promise2 = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ if (sendVerificationEmailCallCount === 1) {
+ promise1.resolve();
+ } else {
+ promise2.resolve();
+ }
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
+ sendMail: () => {},
};
- let serverConfig = {
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('resends_verification_token');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await promise1;
+ const config = Config.get('test');
+ const newUser = await config.database
+ .find('_User', { username: 'resends_verification_token' })
+ .then(results => {
+ return results[0];
+ });
+ // store this user before we make our email request
+ const userBeforeRequest = newUser;
+
+ expect(sendVerificationEmailCallCount).toBe(1);
+
+ const response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: {
+ email: 'user@parse.com',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ });
+ expect(response.status).toBe(200);
+ await promise2;
+ expect(sendVerificationEmailCallCount).toBe(2);
+ expect(sendEmailOptions).toBeDefined();
+
+ // query for this user again
+ const userAfterRequest = await config.database
+ .find('_User', { username: 'resends_verification_token' }, {}, Auth.maintenance(config))
+ .then(results => {
+ return results[0];
+ });
+ // verify that our token & expiration has been changed for this new request
+ expect(typeof userAfterRequest).toBe('object');
+ expect(userBeforeRequest._email_verify_token).not.toEqual(
+ userAfterRequest._email_verify_token
+ );
+ expect(userBeforeRequest._email_verify_token_expires_at).not.toEqual(
+ userAfterRequest._email_verify_token_expires_at
+ );
+ });
+
+ it('provides function arguments in verifyUserEmails on verificationEmailRequest', async () => {
+ const user = new Parse.User();
+ user.setUsername('user');
+ user.setPassword('pass');
+ user.set('email', 'test@example.com');
+ await user.signUp();
+
+ const verifyUserEmails = {
+ method: async (params) => {
+ expect(params.object).toBeInstanceOf(Parse.User);
+ expect(params.ip).toBeDefined();
+ expect(params.master).toBeDefined();
+ expect(params.installationId).toBeDefined();
+ expect(params.resendRequest).toBeTrue();
+ expect(params.createdWith).toBeUndefined();
+ return true;
+ },
};
+ const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough();
+ await reconfigureServer({
+ appName: 'test',
+ publicServerURL: 'http://localhost:1337/1',
+ verifyUserEmails: verifyUserEmails.method,
+ preventLoginWithUnverifiedEmail: verifyUserEmails.method,
+ preventSignupWithUnverifiedEmail: true,
+ emailAdapter: MockEmailAdapterWithOptions({
+ fromAddress: 'parse@example.com',
+ apiKey: 'k',
+ domain: 'd',
+ }),
+ });
- reconfigureServer(serverConfig)
- .then(() => {
- user.setUsername("newEmailVerifyTokenOnEmailReset");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
- })
- .then(() => {
- return MongoClient.connect(databaseURI);
- })
- .then(database => {
- expect(typeof database).toBe('object');
- db = database; //save the db object for later use
- return db.collection('test__User').findOne({username: 'newEmailVerifyTokenOnEmailReset'});
- })
- .then(userFromDb => {
- expect(typeof userFromDb).toBe('object');
- userBeforeEmailReset = userFromDb;
-
- // trigger another token generation by setting the email
- user.set('email', 'user@parse.com');
- return new Promise((resolve, reject) => {
- // wait for half a sec to get a new expiration time
- setTimeout( () => resolve(user.save()), 500 );
+ await expectAsync(Parse.User.requestEmailVerification('test@example.com')).toBeResolved();
+ expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('should throw with invalid emailVerifyTokenReuseIfValid', async () => {
+ const sendEmailOptions = [];
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: options => {
+ sendEmailOptions.push(options);
+ },
+ sendMail: () => {},
+ };
+ try {
+ await reconfigureServer({
+ appName: 'passwordPolicy',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes
+ emailVerifyTokenReuseIfValid: [],
+ publicServerURL: 'http://localhost:8378/1',
});
- })
- .then(() => {
- // get user data after email reset and new token generation
- return db.collection('test__User').findOne({username: 'newEmailVerifyTokenOnEmailReset'});
- })
- .then(userAfterEmailReset => {
- expect(typeof userAfterEmailReset).toBe('object');
- expect(userBeforeEmailReset._email_verify_token).not.toEqual(userAfterEmailReset._email_verify_token);
- expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual(userAfterEmailReset.__email_verify_token_expires_at);
- done();
- })
- .catch((err) => {
- fail("this should not fail");
- done();
+ fail('should have thrown.');
+ } catch (e) {
+ expect(e).toBe('emailVerifyTokenReuseIfValid must be a boolean value');
+ }
+ try {
+ await reconfigureServer({
+ appName: 'passwordPolicy',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenReuseIfValid: true,
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ fail('should have thrown.');
+ } catch (e) {
+ expect(e).toBe(
+ 'You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration'
+ );
+ }
+ });
+
+ it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)('should match codes with emailVerifyTokenReuseIfValid', async () => {
+ let sendEmailOptions;
+ let sendVerificationEmailCallCount = 0;
+ const promise1 = resolvingPromise();
+ const promise2 = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ if (sendVerificationEmailCallCount === 1) {
+ promise1.resolve();
+ } else {
+ promise2.resolve();
+ }
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes
+ publicServerURL: 'http://localhost:8378/1',
+ emailVerifyTokenReuseIfValid: true,
+ });
+ const user = new Parse.User();
+ user.setUsername('resends_verification_token');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@example.com');
+ await user.signUp();
+ await promise1;
+ const config = Config.get('test');
+ const [userBeforeRequest] = await config.database.find('_User', {
+ username: 'resends_verification_token',
+ }, {}, Auth.maintenance(config));
+ // store this user before we make our email request
+ expect(sendVerificationEmailCallCount).toBe(1);
+ await new Promise(resolve => {
+ setTimeout(() => {
+ resolve();
+ }, 1000);
+ });
+ const response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: {
+ email: 'user@example.com',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
});
+ await promise2;
+ expect(response.status).toBe(200);
+ expect(sendVerificationEmailCallCount).toBe(2);
+ expect(sendEmailOptions).toBeDefined();
+
+ const [userAfterRequest] = await config.database.find('_User', {
+ username: 'resends_verification_token',
+ }, {}, Auth.maintenance(config));
+
+ // Verify that token & expiration haven't been changed for this new request
+ expect(typeof userAfterRequest).toBe('object');
+ expect(userBeforeRequest._email_verify_token).toBeDefined();
+ expect(userBeforeRequest._email_verify_token).toEqual(userAfterRequest._email_verify_token);
+ expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined();
+ expect(userBeforeRequest._email_verify_token_expires_at).toEqual(userAfterRequest._email_verify_token_expires_at);
});
- it_exclude_dbs(['postgres'])('client should not see the _email_verify_token_expires_at field', done => {
- var user = new Parse.User();
- var sendEmailOptions;
- var emailAdapter = {
+ it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)('should not send a new verification email when a resend is requested and the user is VERIFIED', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ let sendVerificationEmailCallCount = 0;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ sendPromise.resolve();
},
sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => {}
- }
- reconfigureServer({
+ sendMail: () => {},
+ };
+ await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5, // 5 seconds
- publicServerURL: "http://localhost:8378/1"
- })
- .then(() => {
- user.setUsername("testEmailVerifyTokenValidity");
- user.setPassword("expiringToken");
- user.set('email', 'user@parse.com');
- return user.signUp();
+ publicServerURL: 'http://localhost:8378/1',
+ emailVerifySuccessOnInvalidEmail: false,
+ });
+ user.setUsername('no_new_verification_token_once_verified');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ let response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ expect(sendVerificationEmailCallCount).toBe(1);
+
+ response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: {
+ email: 'user@parse.com',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ }).then(fail, res => res);
+ expect(response.status).toBe(400);
+ expect(sendVerificationEmailCallCount).toBe(1);
+ });
+
+ it('should not send a new verification email if this user does not exist', async () => {
+ let sendEmailOptions;
+ let sendVerificationEmailCallCount = 0;
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ emailVerifySuccessOnInvalidEmail: false,
+ });
+ const response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: {
+ email: 'user@parse.com',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
})
- .then(() => {
-
- user.fetch()
- .then(() => {
- expect(user.get('emailVerified')).toEqual(false);
- expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined');
- done();
- })
- .catch(error => {
- fail("this should not fail");
- done();
- });
+ .then(fail)
+ .catch(response => response);
+
+ expect(response.status).toBe(400);
+ expect(sendVerificationEmailCallCount).toBe(0);
+ expect(sendEmailOptions).not.toBeDefined();
+ });
+
+ it('should fail if no email is supplied', async () => {
+ let sendEmailOptions;
+ let sendVerificationEmailCallCount = 0;
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ const response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: {},
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ }).then(fail, response => response);
+ expect(response.status).toBe(400);
+ expect(response.data.code).toBe(Parse.Error.EMAIL_MISSING);
+ expect(response.data.error).toBe('you must provide an email');
+ expect(sendVerificationEmailCallCount).toBe(0);
+ expect(sendEmailOptions).not.toBeDefined();
+ });
+ it('should fail if email is not a string', async () => {
+ let sendEmailOptions;
+ let sendVerificationEmailCallCount = 0;
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendVerificationEmailCallCount++;
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
});
+ const response = await request({
+ url: 'http://localhost:8378/1/verificationEmailRequest',
+ method: 'POST',
+ body: { email: 3 },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-REST-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ },
+ }).then(fail, res => res);
+ expect(response.status).toBe(400);
+ expect(response.data.code).toBe(Parse.Error.INVALID_EMAIL_ADDRESS);
+ expect(response.data.error).toBe('you must provide a valid email string');
+ expect(sendVerificationEmailCallCount).toBe(0);
+ expect(sendEmailOptions).not.toBeDefined();
});
-})
+ it('client should not see the _email_verify_token_expires_at field', async () => {
+ const user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ await user.fetch();
+ expect(user.get('emailVerified')).toEqual(false);
+ expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined');
+ expect(sendEmailOptions).toBeDefined();
+ });
+
+ it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)('emailVerified should be set to false after changing from an already verified email', async () => {
+ let user = new Parse.User();
+ let sendEmailOptions;
+ const sendPromise = resolvingPromise();
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ sendPromise.resolve();
+ },
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ user.setUsername('testEmailVerifyTokenValidity');
+ user.setPassword('expiringToken');
+ user.set('email', 'user@parse.com');
+ await user.signUp();
+ await sendPromise;
+ let response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ user = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken');
+ expect(typeof user).toBe('object');
+ expect(user.get('emailVerified')).toBe(true);
+
+ user.set('email', 'newEmail@parse.com');
+ await user.save();
+ await user.fetch();
+ expect(typeof user).toBe('object');
+ expect(user.get('email')).toBe('newEmail@parse.com');
+ expect(user.get('emailVerified')).toBe(false);
+
+ response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ });
+});
diff --git a/spec/EnableExpressErrorHandler.spec.js b/spec/EnableExpressErrorHandler.spec.js
new file mode 100644
index 0000000000..64c250628b
--- /dev/null
+++ b/spec/EnableExpressErrorHandler.spec.js
@@ -0,0 +1,32 @@
+const request = require('../lib/request');
+
+describe('Enable express error handler', () => {
+ it('should call the default handler in case of error, like updating a non existing object', async done => {
+ spyOn(console, 'error');
+ const parseServer = await reconfigureServer({
+ enableExpressErrorHandler: true,
+ });
+ parseServer.app.use(function (err, req, res, next) {
+ expect(err.message).toBe('Object not found.');
+ next(err);
+ });
+
+ try {
+ await request({
+ method: 'PUT',
+ url: defaultConfiguration.serverURL + '/classes/AnyClass/nonExistingId',
+ headers: {
+ 'X-Parse-Application-Id': defaultConfiguration.appId,
+ 'X-Parse-Master-Key': defaultConfiguration.masterKey,
+ 'Content-Type': 'application/json',
+ },
+ body: { someField: 'blablabla' },
+ });
+ fail('Should throw error');
+ } catch (response) {
+ expect(response).toBeDefined();
+ expect(response.status).toEqual(500);
+ parseServer.server.close(done);
+ }
+ });
+});
diff --git a/spec/EventEmitterPubSub.spec.js b/spec/EventEmitterPubSub.spec.js
index c457215be8..00358646de 100644
--- a/spec/EventEmitterPubSub.spec.js
+++ b/spec/EventEmitterPubSub.spec.js
@@ -1,13 +1,13 @@
-var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
+const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
-describe('EventEmitterPubSub', function() {
- it('can publish and subscribe', function() {
- var publisher = EventEmitterPubSub.createPublisher();
- var subscriber = EventEmitterPubSub.createSubscriber();
+describe('EventEmitterPubSub', function () {
+ it('can publish and subscribe', function () {
+ const publisher = EventEmitterPubSub.createPublisher();
+ const subscriber = EventEmitterPubSub.createSubscriber();
subscriber.subscribe('testChannel');
// Register mock checked for subscriber
- var isChecked = false;
- subscriber.on('message', function(channel, message) {
+ let isChecked = false;
+ subscriber.on('message', function (channel, message) {
isChecked = true;
expect(channel).toBe('testChannel');
expect(message).toBe('testMessage');
@@ -18,14 +18,14 @@ describe('EventEmitterPubSub', function() {
expect(isChecked).toBe(true);
});
- it('can unsubscribe', function() {
- var publisher = EventEmitterPubSub.createPublisher();
- var subscriber = EventEmitterPubSub.createSubscriber();
+ it('can unsubscribe', function () {
+ const publisher = EventEmitterPubSub.createPublisher();
+ const subscriber = EventEmitterPubSub.createSubscriber();
subscriber.subscribe('testChannel');
subscriber.unsubscribe('testChannel');
// Register mock checked for subscriber
- var isCalled = false;
- subscriber.on('message', function(channel, message) {
+ let isCalled = false;
+ subscriber.on('message', function () {
isCalled = true;
});
@@ -34,8 +34,8 @@ describe('EventEmitterPubSub', function() {
expect(isCalled).toBe(false);
});
- it('can unsubscribe not subscribing channel', function() {
- var subscriber = EventEmitterPubSub.createSubscriber();
+ it('can unsubscribe not subscribing channel', function () {
+ const subscriber = EventEmitterPubSub.createSubscriber();
// Make sure subscriber does not throw exception
subscriber.unsubscribe('testChannel');
diff --git a/spec/FileDownload.spec.js b/spec/FileDownload.spec.js
new file mode 100644
index 0000000000..5010f032ee
--- /dev/null
+++ b/spec/FileDownload.spec.js
@@ -0,0 +1,282 @@
+'use strict';
+
+describe('fileDownload', () => {
+ describe('config validation', () => {
+ it('should default all flags to true when fileDownload is undefined', async () => {
+ await reconfigureServer({ fileDownload: undefined });
+ const Config = require('../lib/Config');
+ const config = Config.get(Parse.applicationId);
+ expect(config.fileDownload.enableForAnonymousUser).toBe(true);
+ expect(config.fileDownload.enableForAuthenticatedUser).toBe(true);
+ expect(config.fileDownload.enableForPublic).toBe(true);
+ });
+
+ it('should accept valid boolean values', async () => {
+ await reconfigureServer({
+ fileDownload: {
+ enableForAnonymousUser: false,
+ enableForAuthenticatedUser: false,
+ enableForPublic: false,
+ },
+ });
+ const Config = require('../lib/Config');
+ const config = Config.get(Parse.applicationId);
+ expect(config.fileDownload.enableForAnonymousUser).toBe(false);
+ expect(config.fileDownload.enableForAuthenticatedUser).toBe(false);
+ expect(config.fileDownload.enableForPublic).toBe(false);
+ });
+
+ it('should reject non-object values', async () => {
+ for (const value of ['string', 123, true, []]) {
+ await expectAsync(reconfigureServer({ fileDownload: value })).toBeRejected();
+ }
+ });
+
+ it('should reject non-boolean flag values', async () => {
+ await expectAsync(
+ reconfigureServer({ fileDownload: { enableForAnonymousUser: 'yes' } })
+ ).toBeRejected();
+ await expectAsync(
+ reconfigureServer({ fileDownload: { enableForAuthenticatedUser: 1 } })
+ ).toBeRejected();
+ await expectAsync(
+ reconfigureServer({ fileDownload: { enableForPublic: null } })
+ ).toBeRejected();
+ });
+ });
+
+ describe('permissions', () => {
+ async function uploadTestFile() {
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'Content-Type': 'text/plain',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Master-Key': 'test',
+ },
+ method: 'POST',
+ url: 'http://localhost:8378/1/files/test.txt',
+ body: 'hello world',
+ });
+ return res.data;
+ }
+
+ it('should allow public download by default', async () => {
+ await reconfigureServer();
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ const res = await request({
+ method: 'GET',
+ url: file.url,
+ });
+ expect(res.status).toBe(200);
+ });
+
+ it('should block public download when enableForPublic is false', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForPublic: false },
+ });
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ try {
+ await request({
+ method: 'GET',
+ url: file.url,
+ });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+ }
+ });
+
+ it('should allow authenticated user download when enableForAuthenticatedUser is true', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForPublic: false, enableForAuthenticatedUser: true },
+ });
+ const file = await uploadTestFile();
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'testpass');
+ await user.signUp();
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ expect(res.status).toBe(200);
+ });
+
+ it('should block authenticated user download when enableForAuthenticatedUser is false', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForAuthenticatedUser: false },
+ });
+ const file = await uploadTestFile();
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'testpass');
+ await user.signUp();
+ const request = require('../lib/request');
+ try {
+ await request({
+ headers: {
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+ }
+ });
+
+ it('should block anonymous user download when enableForAnonymousUser is false', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForAnonymousUser: false },
+ });
+ const file = await uploadTestFile();
+ const user = await Parse.AnonymousUtils.logIn();
+ const request = require('../lib/request');
+ try {
+ await request({
+ headers: {
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+ }
+ });
+
+ it('should allow anonymous user download when enableForAnonymousUser is true', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForAnonymousUser: true, enableForPublic: false },
+ });
+ const file = await uploadTestFile();
+ const user = await Parse.AnonymousUtils.logIn();
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'X-Parse-Session-Token': user.getSessionToken(),
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ expect(res.status).toBe(200);
+ });
+
+ it('should allow master key to bypass all restrictions', async () => {
+ await reconfigureServer({
+ fileDownload: {
+ enableForAnonymousUser: false,
+ enableForAuthenticatedUser: false,
+ enableForPublic: false,
+ },
+ });
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'X-Parse-Master-Key': 'test',
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ expect(res.status).toBe(200);
+ });
+
+ it('should block metadata endpoint when download is disabled for public', async () => {
+ await reconfigureServer({
+ fileDownload: { enableForPublic: false },
+ });
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ // The file URL is like http://localhost:8378/1/files/test/abc_test.txt
+ // The metadata URL replaces /files/APPID/ with /files/APPID/metadata/
+ const url = new URL(file.url);
+ const pathParts = url.pathname.split('/');
+ // pathParts: ['', '1', 'files', 'test', 'abc_test.txt']
+ const appIdIndex = pathParts.indexOf('files') + 1;
+ pathParts.splice(appIdIndex + 1, 0, 'metadata');
+ url.pathname = pathParts.join('/');
+ try {
+ await request({
+ method: 'GET',
+ url: url.toString(),
+ });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+ }
+ });
+
+ it('should block all downloads when all flags are false', async () => {
+ await reconfigureServer({
+ fileDownload: {
+ enableForAnonymousUser: false,
+ enableForAuthenticatedUser: false,
+ enableForPublic: false,
+ },
+ });
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ try {
+ await request({
+ method: 'GET',
+ url: file.url,
+ });
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
+ }
+ });
+
+ it('should allow maintenance key to bypass download restrictions', async () => {
+ await reconfigureServer({
+ fileDownload: {
+ enableForAnonymousUser: false,
+ enableForAuthenticatedUser: false,
+ enableForPublic: false,
+ },
+ });
+ const file = await uploadTestFile();
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'X-Parse-Maintenance-Key': 'testing',
+ },
+ method: 'GET',
+ url: file.url,
+ });
+ expect(res.status).toBe(200);
+ });
+
+ it('should allow maintenance key to bypass upload restrictions', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ enableForAnonymousUser: false,
+ enableForAuthenticatedUser: false,
+ enableForPublic: false,
+ },
+ });
+ const request = require('../lib/request');
+ const res = await request({
+ headers: {
+ 'Content-Type': 'text/plain',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Maintenance-Key': 'testing',
+ },
+ method: 'POST',
+ url: 'http://localhost:8378/1/files/test.txt',
+ body: 'hello world',
+ });
+ expect(res.data.url).toBeDefined();
+ });
+ });
+});
diff --git a/spec/FileLoggerAdapter.spec.js b/spec/FileLoggerAdapter.spec.js
deleted file mode 100644
index 2816e95c34..0000000000
--- a/spec/FileLoggerAdapter.spec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-'use strict';
-
-var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
-var Parse = require('parse/node').Parse;
-var request = require('request');
-
-describe('info logs', () => {
- it("Verify INFO logs", (done) => {
- var fileLoggerAdapter = new FileLoggerAdapter();
- fileLoggerAdapter.info('testing info logs', () => {
- fileLoggerAdapter.query({
- from: new Date(Date.now() - 500),
- size: 100,
- level: 'info'
- }, (results) => {
- if(results.length == 0) {
- fail('The adapter should return non-empty results');
- done();
- } else {
- expect(results[0].message).toEqual('testing info logs');
- done();
- }
- });
- });
- });
-});
-
-describe('error logs', () => {
- it("Verify ERROR logs", (done) => {
- var fileLoggerAdapter = new FileLoggerAdapter();
- fileLoggerAdapter.error('testing error logs', () => {
- fileLoggerAdapter.query({
- from: new Date(Date.now() - 500),
- size: 100,
- level: 'error'
- }, (results) => {
- if(results.length == 0) {
- fail('The adapter should return non-empty results');
- done();
- }
- else {
- expect(results[0].message).toEqual('testing error logs');
- done();
- }
- });
- });
- });
-});
-
-describe('verbose logs', () => {
- it("mask sensitive information in _User class", (done) => {
- reconfigureServer({ verbose: true })
- .then(() => createTestUser())
- .then(() => {
- let fileLoggerAdapter = new FileLoggerAdapter();
- return fileLoggerAdapter.query({
- from: new Date(Date.now() - 500),
- size: 100,
- level: 'verbose'
- });
- }).then((results) => {
- let logString = JSON.stringify(results);
- expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0);
- expect(logString.match(/moon-y/g)).toBe(null);
-
- var headers = {
- 'X-Parse-Application-Id': 'test',
- 'X-Parse-REST-API-Key': 'rest'
- };
- request.get({
- headers: headers,
- url: 'http://localhost:8378/1/login?username=test&password=moon-y'
- }, (error, response, body) => {
- let fileLoggerAdapter = new FileLoggerAdapter();
- return fileLoggerAdapter.query({
- from: new Date(Date.now() - 500),
- size: 100,
- level: 'verbose'
- }).then((results) => {
- let logString = JSON.stringify(results);
- expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0);
- expect(logString.match(/moon-y/g)).toBe(null);
- done();
- });
- });
- }).catch((err) =>Â {
- fail(JSON.stringify(err));
- done();
- })
- });
-
- it("should not mask information in non _User class", (done) => {
- let obj = new Parse.Object('users');
- obj.set('password', 'pw');
- obj.save().then(() => {
- let fileLoggerAdapter = new FileLoggerAdapter();
- return fileLoggerAdapter.query({
- from: new Date(Date.now() - 500),
- size: 100,
- level: 'verbose'
- });
- }).then((results) => {
- expect(results[1].body.password).toEqual("pw");
- done();
- });
- });
-});
diff --git a/spec/FileUrlValidator.spec.js b/spec/FileUrlValidator.spec.js
new file mode 100644
index 0000000000..886aaf75e3
--- /dev/null
+++ b/spec/FileUrlValidator.spec.js
@@ -0,0 +1,141 @@
+'use strict';
+
+const { validateFileUrl, validateFileUrlsInObject } = require('../src/FileUrlValidator');
+
+describe('FileUrlValidator', () => {
+ describe('validateFileUrl', () => {
+ it('allows null, undefined, and empty string URLs', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: [] } };
+ expect(() => validateFileUrl(null, config)).not.toThrow();
+ expect(() => validateFileUrl(undefined, config)).not.toThrow();
+ expect(() => validateFileUrl('', config)).not.toThrow();
+ });
+
+ it('allows any URL when allowedFileUrlDomains contains wildcard', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['*'] } };
+ expect(() => validateFileUrl('http://malicious.example.com/file.txt', config)).not.toThrow();
+ expect(() => validateFileUrl('http://malicious.example.com/leak', config)).not.toThrow();
+ });
+
+ it('allows any URL when allowedFileUrlDomains is not an array', () => {
+ expect(() => validateFileUrl('http://example.com/file', {})).not.toThrow();
+ expect(() => validateFileUrl('http://example.com/file', { fileUpload: {} })).not.toThrow();
+ expect(() => validateFileUrl('http://example.com/file', null)).not.toThrow();
+ });
+
+ it('rejects all URLs when allowedFileUrlDomains is empty', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: [] } };
+ expect(() => validateFileUrl('http://example.com/file', config)).toThrowError(
+ /not allowed/
+ );
+ });
+
+ it('allows URLs matching exact hostname', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['cdn.example.com'] } };
+ expect(() => validateFileUrl('https://cdn.example.com/files/test.txt', config)).not.toThrow();
+ });
+
+ it('rejects URLs not matching any allowed hostname', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['cdn.example.com'] } };
+ expect(() => validateFileUrl('http://malicious.example.com/file', config)).toThrowError(
+ /not allowed/
+ );
+ });
+
+ it('supports wildcard subdomain matching', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['*.example.com'] } };
+ expect(() => validateFileUrl('https://cdn.example.com/file.txt', config)).not.toThrow();
+ expect(() => validateFileUrl('https://us-east.cdn.example.com/file.txt', config)).not.toThrow();
+ expect(() => validateFileUrl('https://example.net/file.txt', config)).toThrowError(
+ /not allowed/
+ );
+ });
+
+ it('performs case-insensitive hostname matching', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['CDN.Example.COM'] } };
+ expect(() => validateFileUrl('https://cdn.example.com/file.txt', config)).not.toThrow();
+ });
+
+ it('throws on invalid URL strings', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['example.com'] } };
+ expect(() => validateFileUrl('not-a-url', config)).toThrowError(
+ /Invalid file URL/
+ );
+ });
+
+ it('supports multiple allowed domains', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['cdn1.example.com', 'cdn2.example.com'] } };
+ expect(() => validateFileUrl('https://cdn1.example.com/file.txt', config)).not.toThrow();
+ expect(() => validateFileUrl('https://cdn2.example.com/file.txt', config)).not.toThrow();
+ expect(() => validateFileUrl('https://cdn3.example.com/file.txt', config)).toThrowError(
+ /not allowed/
+ );
+ });
+
+ it('does not allow partial hostname matches', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['example.com'] } };
+ expect(() => validateFileUrl('https://notexample.com/file.txt', config)).toThrowError(
+ /not allowed/
+ );
+ expect(() => validateFileUrl('https://example.com.malicious.example.com/file.txt', config)).toThrowError(
+ /not allowed/
+ );
+ });
+ });
+
+ describe('validateFileUrlsInObject', () => {
+ const config = { fileUpload: { allowedFileUrlDomains: ['example.com'] } };
+
+ it('validates file URLs in flat objects', () => {
+ expect(() =>
+ validateFileUrlsInObject(
+ { file: { __type: 'File', name: 'test.txt', url: 'http://malicious.example.com/file' } },
+ config
+ )
+ ).toThrowError(/not allowed/);
+ });
+
+ it('validates file URLs in nested objects', () => {
+ expect(() =>
+ validateFileUrlsInObject(
+ { nested: { deep: { file: { __type: 'File', name: 'test.txt', url: 'http://malicious.example.com/file' } } } },
+ config
+ )
+ ).toThrowError(/not allowed/);
+ });
+
+ it('validates file URLs in arrays', () => {
+ expect(() =>
+ validateFileUrlsInObject(
+ [{ __type: 'File', name: 'test.txt', url: 'http://malicious.example.com/file' }],
+ config
+ )
+ ).toThrowError(/not allowed/);
+ });
+
+ it('allows files without URLs', () => {
+ expect(() =>
+ validateFileUrlsInObject(
+ { file: { __type: 'File', name: 'test.txt' } },
+ config
+ )
+ ).not.toThrow();
+ });
+
+ it('allows files with permitted URLs', () => {
+ expect(() =>
+ validateFileUrlsInObject(
+ { file: { __type: 'File', name: 'test.txt', url: 'http://example.com/file.txt' } },
+ config
+ )
+ ).not.toThrow();
+ });
+
+ it('handles null, undefined, and primitive values', () => {
+ expect(() => validateFileUrlsInObject(null, config)).not.toThrow();
+ expect(() => validateFileUrlsInObject(undefined, config)).not.toThrow();
+ expect(() => validateFileUrlsInObject('string', config)).not.toThrow();
+ expect(() => validateFileUrlsInObject(42, config)).not.toThrow();
+ });
+ });
+});
diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js
index d6e9e4cf43..30acf7d13c 100644
--- a/spec/FilesController.spec.js
+++ b/spec/FilesController.spec.js
@@ -1,30 +1,221 @@
-var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
-var Config = require("../src/Config");
-var FilesController = require('../src/Controllers/FilesController').default;
+const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
+const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
+ .WinstonLoggerAdapter;
+const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
+ .GridFSBucketAdapter;
+const Config = require('../lib/Config');
+const FilesController = require('../lib/Controllers/FilesController').default;
+const databaseURI = 'mongodb://localhost:27017/parse';
+const mockAdapter = {
+ createFile: () => {
+ return Promise.reject(new Error('it failed with xyz'));
+ },
+ deleteFile: () => {},
+ getFileData: () => {},
+ getFileLocation: () => 'xyz',
+ validateFilename: () => {
+ return null;
+ },
+};
// Small additional tests to improve overall coverage
-describe("FilesController",() =>{
- it("should properly expand objects", (done) => {
-
- var config = new Config(Parse.applicationId);
- var gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
- var filesController = new FilesController(gridStoreAdapter)
- var result = filesController.expandFilesInObject(config, function(){});
+describe('FilesController', () => {
+ it('should properly expand objects with sync getFileLocation', async () => {
+ const config = Config.get(Parse.applicationId);
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ gridFSAdapter.getFileLocation = (config, filename) => {
+ return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
+ }
+ const filesController = new FilesController(gridFSAdapter);
+ const result = await filesController.expandFilesInObject(config, function () { });
expect(result).toBeUndefined();
- var fullFile = {
+ const fullFile = {
type: '__type',
- url: "http://an.url"
- }
+ url: 'http://an.url',
+ };
- var anObject = {
- aFile: fullFile
+ const anObject = {
+ aFile: fullFile,
+ };
+ await filesController.expandFilesInObject(config, anObject);
+ expect(anObject.aFile.url).toEqual('http://an.url');
+ });
+
+ it('should properly expand objects with async getFileLocation', async () => {
+ const config = Config.get(Parse.applicationId);
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ gridFSAdapter.getFileLocation = async (config, filename) => {
+ await Promise.resolve();
+ return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
}
- filesController.expandFilesInObject(config, anObject);
- expect(anObject.aFile.url).toEqual("http://an.url");
+ const filesController = new FilesController(gridFSAdapter);
+ const result = await filesController.expandFilesInObject(config, function () { });
+
+ expect(result).toBeUndefined();
+
+ const fullFile = {
+ type: '__type',
+ url: 'http://an.url',
+ };
+
+ const anObject = {
+ aFile: fullFile,
+ };
+ await filesController.expandFilesInObject(config, anObject);
+ expect(anObject.aFile.url).toEqual('http://an.url');
+ });
+
+ it('should call getFileLocation when config.fileKey is undefined', async () => {
+ const config = {};
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+
+ const fullFile = {
+ name: 'mock-name',
+ __type: 'File',
+ };
+ gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
+ const filesController = new FilesController(gridFSAdapter);
+
+ const anObject = { aFile: fullFile };
+ await filesController.expandFilesInObject(config, anObject);
+ expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
+ expect(anObject.aFile.url).toEqual('mock-url');
+ });
+
+ it('should call getFileLocation when config.fileKey is defined', async () => {
+ const config = { fileKey: 'mock-key' };
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+
+ const fullFile = {
+ name: 'mock-name',
+ __type: 'File',
+ };
+ gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
+ const filesController = new FilesController(gridFSAdapter);
+
+ const anObject = { aFile: fullFile };
+ await filesController.expandFilesInObject(config, anObject);
+ expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
+ expect(anObject.aFile.url).toEqual('mock-url');
+ });
+
+
+ it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => {
+ await reconfigureServer({
+ databaseURI: 'mongodb://localhost:27017/parse',
+ filesAdapter: null,
+ databaseAdapter: null,
+ databaseOptions: {
+ retryWrites: true,
+ },
+ });
+ const config = Config.get(Parse.applicationId);
+ expect(config.database.adapter._mongoOptions.retryWrites).toBeTrue();
+ expect(config.filesController.adapter._mongoOptions.retryWrites).toBeTrue();
+ expect(config.filesController.adapter._mongoOptions.enableSchemaHooks).toBeUndefined();
+ expect(config.filesController.adapter._mongoOptions.schemaCacheTtl).toBeUndefined();
+ });
+
+ it('should create a server log on failure', done => {
+ const logController = new LoggerController(new WinstonLoggerAdapter());
+
+ reconfigureServer({ filesAdapter: mockAdapter })
+ .then(() => new Parse.File('yolo.txt', [1, 2, 3], 'text/plain').save())
+ .then(
+ () => done.fail('should not succeed'),
+ () => setImmediate(() => Promise.resolve('done'))
+ )
+ .then(() => new Promise(resolve => setTimeout(resolve, 200)))
+ .then(() => logController.getLogs({ from: Date.now() - 1000, size: 1000 }))
+ .then(logs => {
+ // we get two logs here: 1. the source of the failure to save the file
+ // and 2 the message that will be sent back to the client.
+
+ const log1 = logs.find(x => x.message === 'Error creating a file: it failed with xyz');
+ expect(log1.level).toBe('error');
+
+ const log2 = logs.find(x => x.message === 'it failed with xyz');
+ expect(log2.level).toBe('error');
+ expect(log2.code).toBe(130);
+
+ done();
+ });
+ });
+
+ it('should create a parse error when a string is returned', done => {
+ const mock2 = mockAdapter;
+ mock2.validateFilename = () => {
+ return 'Bad file! No biscuit!';
+ };
+ const filesController = new FilesController(mockAdapter);
+ const error = filesController.validateFilename();
+ expect(typeof error).toBe('object');
+ expect(error.message.indexOf('biscuit')).toBe(13);
+ expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME);
+ mockAdapter.validateFilename = () => {
+ return null;
+ };
+ done();
+ });
+
+ it('should add a unique hash to the file name when the preserveFileName option is false', async () => {
+ const config = Config.get(Parse.applicationId);
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ spyOn(gridFSAdapter, 'createFile');
+ gridFSAdapter.createFile.and.returnValue(Promise.resolve());
+ const fileName = 'randomFileName.pdf';
+ const regexEscapedFileName = fileName.replace(/\./g, '\\$&');
+ const filesController = new FilesController(gridFSAdapter, null, {
+ preserveFileName: false,
+ });
+
+ await filesController.createFile(config, fileName);
+
+ expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
+ expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch(
+ `^.{32}_${regexEscapedFileName}$`
+ );
+ });
+
+ it('should not add a unique hash to the file name when the preserveFileName option is true', async () => {
+ const config = Config.get(Parse.applicationId);
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ spyOn(gridFSAdapter, 'createFile');
+ gridFSAdapter.createFile.and.returnValue(Promise.resolve());
+ const fileName = 'randomFileName.pdf';
+ const filesController = new FilesController(gridFSAdapter, null, {
+ preserveFileName: true,
+ });
+
+ await filesController.createFile(config, fileName);
+
+ expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
+ expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
+ });
+
+ it('should handle adapter without getMetadata', async () => {
+ const gridFSAdapter = new GridFSBucketAdapter(databaseURI);
+ gridFSAdapter.getMetadata = null;
+ const filesController = new FilesController(gridFSAdapter);
+
+ const result = await filesController.getMetadata();
+ expect(result).toEqual({});
+ });
+
+ it('should reject slashes in file names', done => {
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ const fileName = 'foo/randomFileName.pdf';
+ expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
+ done();
+ });
+ it('should also reject slashes in file names', done => {
+ const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
+ const fileName = 'foo/randomFileName.pdf';
+ expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
done();
- })
+ });
});
diff --git a/spec/GraphQLQueryComplexity.spec.js b/spec/GraphQLQueryComplexity.spec.js
new file mode 100644
index 0000000000..def95a6b51
--- /dev/null
+++ b/spec/GraphQLQueryComplexity.spec.js
@@ -0,0 +1,247 @@
+'use strict';
+
+const http = require('http');
+const express = require('express');
+const fetch = (...args) =>
+ import('node-fetch').then(({ default: fetch }) => {
+ const [url, options = {}] = args;
+ return fetch(url, { agent: new http.Agent({ keepAlive: false }), ...options });
+ });
+require('./helper');
+const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
+
+describe('graphql query complexity', () => {
+ let httpServer;
+ let graphQLServer;
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Javascript-Key': 'test',
+ 'Content-Type': 'application/json',
+ };
+
+ async function setupGraphQL(serverOptions = {}) {
+ if (httpServer) {
+ await new Promise(resolve => httpServer.close(resolve));
+ }
+ const server = await reconfigureServer(serverOptions);
+ const expressApp = express();
+ httpServer = http.createServer(expressApp);
+ expressApp.use('/parse', server.app);
+ graphQLServer = new ParseGraphQLServer(server, {
+ graphQLPath: '/graphql',
+ });
+ graphQLServer.applyGraphQL(expressApp);
+ await new Promise(resolve => httpServer.listen({ port: 13378 }, resolve));
+ }
+
+ async function graphqlRequest(query, requestHeaders = headers) {
+ const response = await fetch('http://localhost:13378/graphql', {
+ method: 'POST',
+ headers: requestHeaders,
+ body: JSON.stringify({ query }),
+ });
+ return response.json();
+ }
+
+ // Returns a query with depth 4: users(1) > edges(2) > node(3) > objectId(4)
+ function buildDeepQuery() {
+ return '{ users { edges { node { objectId } } } }';
+ }
+
+ function buildWideQuery(fieldCount) {
+ const fields = Array.from({ length: fieldCount }, (_, i) => `field${i}: objectId`).join('\n ');
+ return `{ users { edges { node { ${fields} } } } }`;
+ }
+
+ afterEach(async () => {
+ if (httpServer) {
+ await new Promise(resolve => httpServer.close(resolve));
+ httpServer = null;
+ }
+ });
+
+ describe('depth limit', () => {
+ it('should reject query exceeding depth limit', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: 3 },
+ });
+ const result = await graphqlRequest(buildDeepQuery());
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(
+ /GraphQL query depth of \d+ exceeds maximum allowed depth of 3/
+ );
+ });
+
+ it('should allow query within depth limit', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: 10 },
+ });
+ const result = await graphqlRequest(buildDeepQuery());
+ expect(result.errors).toBeUndefined();
+ });
+
+ it('should allow deep query with master key', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: 3 },
+ });
+ const result = await graphqlRequest(buildDeepQuery(), {
+ ...headers,
+ 'X-Parse-Master-Key': 'test',
+ });
+ expect(result.errors).toBeUndefined();
+ });
+
+ it('should allow unlimited depth when graphQLDepth is -1', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: -1 },
+ });
+ const result = await graphqlRequest(buildDeepQuery());
+ expect(result.errors).toBeUndefined();
+ });
+ });
+
+ describe('fields limit', () => {
+ it('should reject query exceeding fields limit', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 5 },
+ });
+ const result = await graphqlRequest(buildWideQuery(10));
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(
+ /Number of GraphQL fields \(\d+\) exceeds maximum allowed \(5\)/
+ );
+ });
+
+ it('should allow query within fields limit', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 200 },
+ });
+ const result = await graphqlRequest(buildDeepQuery());
+ expect(result.errors).toBeUndefined();
+ });
+
+ it('should allow wide query with master key', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 5 },
+ });
+ const result = await graphqlRequest(buildWideQuery(10), {
+ ...headers,
+ 'X-Parse-Master-Key': 'test',
+ });
+ expect(result.errors).toBeUndefined();
+ });
+
+ it('should count fragment fields at each spread location', async () => {
+ // With correct counting: 2 aliases (2) + 2Ãedges (2) + 2Ãnode (2) + 2ÃobjectId from fragment (2) = 8
+ // With incorrect counting (fragment once): 2 + 2 + 2 + 1 = 7
+ // Set limit to 7 so incorrect counting passes but correct counting rejects
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 7 },
+ });
+ const result = await graphqlRequest(`
+ fragment UserFields on User { objectId }
+ {
+ a1: users { edges { node { ...UserFields } } }
+ a2: users { edges { node { ...UserFields } } }
+ }
+ `);
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(
+ /Number of GraphQL fields \(\d+\) exceeds maximum allowed \(7\)/
+ );
+ });
+
+ it('should count inline fragment fields toward depth and field limits', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 3 },
+ });
+ // Inline fragment adds fields without increasing depth:
+ // users(1) > edges(2) > ... on UserConnection { edges(3) > node(4) }
+ const result = await graphqlRequest(`{
+ users {
+ edges {
+ ... on UserEdge {
+ node {
+ objectId
+ }
+ }
+ }
+ }
+ }`);
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(
+ /Number of GraphQL fields \(\d+\) exceeds maximum allowed \(3\)/
+ );
+ });
+
+ it('should allow unlimited fields when graphQLFields is -1', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: -1 },
+ });
+ const result = await graphqlRequest(buildWideQuery(50));
+ expect(result.errors).toBeUndefined();
+ });
+ });
+
+ describe('fragment fan-out', () => {
+ it('should reject query with exponential fragment fan-out efficiently', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLFields: 100 },
+ });
+ // Binary fan-out: each fragment spreads the next one twice.
+ // Without fix: 2^(levels-1) field visits = 2^25 â 33M (hangs event loop).
+ // With fix (memoization): O(levels) traversal, same field count, instant rejection.
+ const levels = 26;
+ let query = 'query Q { ...F0 }\n';
+ for (let i = 0; i < levels; i++) {
+ if (i === levels - 1) {
+ query += `fragment F${i} on Query { __typename }\n`;
+ } else {
+ query += `fragment F${i} on Query { ...F${i + 1} ...F${i + 1} }\n`;
+ }
+ }
+ const start = Date.now();
+ const result = await graphqlRequest(query);
+ const elapsed = Date.now() - start;
+ // Must complete in under 5 seconds (without fix it would take seconds or hang)
+ expect(elapsed).toBeLessThan(5000);
+ // Field count is 2^(levels-1) = 16777216, which exceeds the limit of 100
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(/Number of GraphQL fields .* exceeds maximum allowed/);
+ });
+ });
+
+ describe('where argument breadth', () => {
+ it('should enforce depth and field limits regardless of where argument breadth', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: 3, graphQLFields: 200, subqueryDepth: 1 },
+ });
+ // The GraphQL where argument may contain many OR branches, but the
+ // complexity check correctly measures the selection set depth/fields,
+ // not the where variable content. A query exceeding graphQLDepth is
+ // rejected even when the where argument is simple.
+ const result = await graphqlRequest(buildDeepQuery());
+ expect(result.errors).toBeDefined();
+ expect(result.errors[0].message).toMatch(
+ /GraphQL query depth of \d+ exceeds maximum allowed depth of 3/
+ );
+ });
+
+ it('should allow query with wide where argument when selection set is within limits', async () => {
+ await setupGraphQL({
+ requestComplexity: { graphQLDepth: 10, graphQLFields: 200, subqueryDepth: 1 },
+ });
+
+ const obj = new Parse.Object('TestItem');
+ obj.set('name', 'test');
+ await obj.save();
+
+ // Wide where with many OR branches â complexity check measures selection
+ // set depth and field count, not where argument structure
+ const orBranches = Array.from({ length: 20 }, (_, i) => `{ name: { equalTo: "test${i}" } }`).join(', ');
+ const query = `{ testItems(where: { OR: [${orBranches}] }) { edges { node { objectId } } } }`;
+ const result = await graphqlRequest(query);
+ expect(result.errors).toBeUndefined();
+ });
+ });
+});
diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js
new file mode 100644
index 0000000000..48fbd09115
--- /dev/null
+++ b/spec/GridFSBucketStorageAdapter.spec.js
@@ -0,0 +1,535 @@
+const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
+ .GridFSBucketAdapter;
+const { randomString } = require('../lib/cryptoUtils');
+const databaseURI = 'mongodb://localhost:27017/parse';
+const request = require('../lib/request');
+
+async function expectMissingFile(gfsAdapter, name) {
+ try {
+ await gfsAdapter.getFileData(name);
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toEqual('FileNotFound: file myFileName was not found');
+ }
+}
+
+describe_only_db('mongo')('GridFSBucket', () => {
+ beforeEach(async () => {
+ const gsAdapter = new GridFSBucketAdapter(databaseURI);
+ const db = await gsAdapter._connect();
+ await db.dropDatabase();
+ });
+
+ it('should connect to mongo with the supported database options', async () => {
+ const databaseURI = 'mongodb://localhost:27017/parse';
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI, {
+ retryWrites: true,
+ // Parse Server-specific options that should be filtered out before passing to MongoDB client
+ allowPublicExplain: true,
+ enableSchemaHooks: true,
+ schemaCacheTtl: 5000,
+ maxTimeMS: 30000,
+ batchSize: 500,
+ disableIndexFieldValidation: true,
+ logClientEvents: [{ name: 'commandStarted' }],
+ createIndexUserUsername: true,
+ createIndexUserUsernameCaseInsensitive: true,
+ createIndexUserEmail: true,
+ createIndexUserEmailCaseInsensitive: true,
+ createIndexUserEmailVerifyToken: true,
+ createIndexUserPasswordResetToken: true,
+ createIndexRoleName: true,
+ });
+
+ const db = await gfsAdapter._connect();
+ const status = await db.admin().serverStatus();
+ expect(status.connections.current > 0).toEqual(true);
+ expect(db.options?.retryWrites).toEqual(true);
+ });
+
+ it('should store batchSize and filter it from MongoClient options', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI, { batchSize: 500 });
+ expect(gfsAdapter._batchSize).toEqual(500);
+ // Verify batchSize is filtered from MongoClient options
+ expect(gfsAdapter._mongoOptions.batchSize).toBeUndefined();
+ });
+
+ it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => {
+ const unencryptedAdapter = new GridFSBucketAdapter(databaseURI);
+ const encryptedAdapter = new GridFSBucketAdapter(
+ databaseURI,
+ {},
+ '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
+ );
+ await expectMissingFile(encryptedAdapter, 'myFileName');
+ const originalString = 'abcdefghi';
+ await encryptedAdapter.createFile('myFileName', originalString);
+ const unencryptedResult = await unencryptedAdapter.getFileData('myFileName');
+ expect(unencryptedResult.toString('utf8')).not.toBe(originalString);
+ const encryptedResult = await encryptedAdapter.getFileData('myFileName');
+ expect(encryptedResult.toString('utf8')).toBe(originalString);
+ });
+
+ it('should rotate key of all unencrypted GridFS files to encrypted files', async () => {
+ const unencryptedAdapter = new GridFSBucketAdapter(databaseURI);
+ const encryptedAdapter = new GridFSBucketAdapter(
+ databaseURI,
+ {},
+ '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
+ );
+ const fileName1 = 'file1.txt';
+ const data1 = 'hello world';
+ const fileName2 = 'file2.txt';
+ const data2 = 'hello new world';
+ //Store unecrypted files
+ await unencryptedAdapter.createFile(fileName1, data1);
+ const unencryptedResult1 = await unencryptedAdapter.getFileData(fileName1);
+ expect(unencryptedResult1.toString('utf8')).toBe(data1);
+ await unencryptedAdapter.createFile(fileName2, data2);
+ const unencryptedResult2 = await unencryptedAdapter.getFileData(fileName2);
+ expect(unencryptedResult2.toString('utf8')).toBe(data2);
+ //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
+ const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey();
+ expect(rotated.length).toEqual(2);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName1;
+ }).length
+ ).toEqual(1);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName2;
+ }).length
+ ).toEqual(1);
+ expect(notRotated.length).toEqual(0);
+ let result = await encryptedAdapter.getFileData(fileName1);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data1);
+ const encryptedData1 = await unencryptedAdapter.getFileData(fileName1);
+ expect(encryptedData1.toString('utf-8')).not.toEqual(unencryptedResult1);
+ result = await encryptedAdapter.getFileData(fileName2);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data2);
+ const encryptedData2 = await unencryptedAdapter.getFileData(fileName2);
+ expect(encryptedData2.toString('utf-8')).not.toEqual(unencryptedResult2);
+ });
+
+ it('should rotate key of all old encrypted GridFS files to encrypted files', async () => {
+ const oldEncryptionKey = 'oldKeyThatILoved';
+ const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey);
+ const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove');
+ const fileName1 = 'file1.txt';
+ const data1 = 'hello world';
+ const fileName2 = 'file2.txt';
+ const data2 = 'hello new world';
+ //Store unecrypted files
+ await oldEncryptedAdapter.createFile(fileName1, data1);
+ const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1);
+ expect(oldEncryptedResult1.toString('utf8')).toBe(data1);
+ await oldEncryptedAdapter.createFile(fileName2, data2);
+ const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2);
+ expect(oldEncryptedResult2.toString('utf8')).toBe(data2);
+ //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
+ const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({
+ oldKey: oldEncryptionKey,
+ });
+ expect(rotated.length).toEqual(2);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName1;
+ }).length
+ ).toEqual(1);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName2;
+ }).length
+ ).toEqual(1);
+ expect(notRotated.length).toEqual(0);
+ let result = await encryptedAdapter.getFileData(fileName1);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data1);
+ let decryptionError1;
+ let encryptedData1;
+ try {
+ encryptedData1 = await oldEncryptedAdapter.getFileData(fileName1);
+ } catch (err) {
+ decryptionError1 = err;
+ }
+ expect(decryptionError1).toMatch('Error');
+ expect(encryptedData1).toBeUndefined();
+ result = await encryptedAdapter.getFileData(fileName2);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data2);
+ let decryptionError2;
+ let encryptedData2;
+ try {
+ encryptedData2 = await oldEncryptedAdapter.getFileData(fileName2);
+ } catch (err) {
+ decryptionError2 = err;
+ }
+ expect(decryptionError2).toMatch('Error');
+ expect(encryptedData2).toBeUndefined();
+ });
+
+ it('should rotate key of all old encrypted GridFS files to unencrypted files', async () => {
+ const oldEncryptionKey = 'oldKeyThatILoved';
+ const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey);
+ const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI);
+ const fileName1 = 'file1.txt';
+ const data1 = 'hello world';
+ const fileName2 = 'file2.txt';
+ const data2 = 'hello new world';
+ //Store unecrypted files
+ await oldEncryptedAdapter.createFile(fileName1, data1);
+ const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1);
+ expect(oldEncryptedResult1.toString('utf8')).toBe(data1);
+ await oldEncryptedAdapter.createFile(fileName2, data2);
+ const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2);
+ expect(oldEncryptedResult2.toString('utf8')).toBe(data2);
+ //Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter
+ const { rotated, notRotated } = await unEncryptedAdapter.rotateEncryptionKey({
+ oldKey: oldEncryptionKey,
+ });
+ expect(rotated.length).toEqual(2);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName1;
+ }).length
+ ).toEqual(1);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName2;
+ }).length
+ ).toEqual(1);
+ expect(notRotated.length).toEqual(0);
+ let result = await unEncryptedAdapter.getFileData(fileName1);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data1);
+ let decryptionError1;
+ let encryptedData1;
+ try {
+ encryptedData1 = await oldEncryptedAdapter.getFileData(fileName1);
+ } catch (err) {
+ decryptionError1 = err;
+ }
+ expect(decryptionError1).toMatch('Error');
+ expect(encryptedData1).toBeUndefined();
+ result = await unEncryptedAdapter.getFileData(fileName2);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data2);
+ let decryptionError2;
+ let encryptedData2;
+ try {
+ encryptedData2 = await oldEncryptedAdapter.getFileData(fileName2);
+ } catch (err) {
+ decryptionError2 = err;
+ }
+ expect(decryptionError2).toMatch('Error');
+ expect(encryptedData2).toBeUndefined();
+ });
+
+ it('should only encrypt specified fileNames', async () => {
+ const oldEncryptionKey = 'oldKeyThatILoved';
+ const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey);
+ const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove');
+ const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI);
+ const fileName1 = 'file1.txt';
+ const data1 = 'hello world';
+ const fileName2 = 'file2.txt';
+ const data2 = 'hello new world';
+ //Store unecrypted files
+ await oldEncryptedAdapter.createFile(fileName1, data1);
+ const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1);
+ expect(oldEncryptedResult1.toString('utf8')).toBe(data1);
+ await oldEncryptedAdapter.createFile(fileName2, data2);
+ const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2);
+ expect(oldEncryptedResult2.toString('utf8')).toBe(data2);
+ //Inject unecrypted file to see if causes an issue
+ const fileName3 = 'file3.txt';
+ const data3 = 'hello past world';
+ await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8');
+ //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
+ const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({
+ oldKey: oldEncryptionKey,
+ fileNames: [fileName1, fileName2],
+ });
+ expect(rotated.length).toEqual(2);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName1;
+ }).length
+ ).toEqual(1);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName2;
+ }).length
+ ).toEqual(1);
+ expect(notRotated.length).toEqual(0);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName3;
+ }).length
+ ).toEqual(0);
+ let result = await encryptedAdapter.getFileData(fileName1);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data1);
+ let decryptionError1;
+ let encryptedData1;
+ try {
+ encryptedData1 = await oldEncryptedAdapter.getFileData(fileName1);
+ } catch (err) {
+ decryptionError1 = err;
+ }
+ expect(decryptionError1).toMatch('Error');
+ expect(encryptedData1).toBeUndefined();
+ result = await encryptedAdapter.getFileData(fileName2);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data2);
+ let decryptionError2;
+ let encryptedData2;
+ try {
+ encryptedData2 = await oldEncryptedAdapter.getFileData(fileName2);
+ } catch (err) {
+ decryptionError2 = err;
+ }
+ expect(decryptionError2).toMatch('Error');
+ expect(encryptedData2).toBeUndefined();
+ });
+
+ it("should return fileNames of those it can't encrypt with the new key", async () => {
+ const oldEncryptionKey = 'oldKeyThatILoved';
+ const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey);
+ const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove');
+ const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI);
+ const fileName1 = 'file1.txt';
+ const data1 = 'hello world';
+ const fileName2 = 'file2.txt';
+ const data2 = 'hello new world';
+ //Store unecrypted files
+ await oldEncryptedAdapter.createFile(fileName1, data1);
+ const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1);
+ expect(oldEncryptedResult1.toString('utf8')).toBe(data1);
+ await oldEncryptedAdapter.createFile(fileName2, data2);
+ const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2);
+ expect(oldEncryptedResult2.toString('utf8')).toBe(data2);
+ //Inject unecrypted file to see if causes an issue
+ const fileName3 = 'file3.txt';
+ const data3 = 'hello past world';
+ await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8');
+ //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
+ const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({
+ oldKey: oldEncryptionKey,
+ });
+ expect(rotated.length).toEqual(2);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName1;
+ }).length
+ ).toEqual(1);
+ expect(
+ rotated.filter(function (value) {
+ return value === fileName2;
+ }).length
+ ).toEqual(1);
+ expect(notRotated.length).toEqual(1);
+ expect(
+ notRotated.filter(function (value) {
+ return value === fileName3;
+ }).length
+ ).toEqual(1);
+ let result = await encryptedAdapter.getFileData(fileName1);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data1);
+ let decryptionError1;
+ let encryptedData1;
+ try {
+ encryptedData1 = await oldEncryptedAdapter.getFileData(fileName1);
+ } catch (err) {
+ decryptionError1 = err;
+ }
+ expect(decryptionError1).toMatch('Error');
+ expect(encryptedData1).toBeUndefined();
+ result = await encryptedAdapter.getFileData(fileName2);
+ expect(Buffer.isBuffer(result)).toBe(true);
+ expect(result.toString('utf-8')).toEqual(data2);
+ let decryptionError2;
+ let encryptedData2;
+ try {
+ encryptedData2 = await oldEncryptedAdapter.getFileData(fileName2);
+ } catch (err) {
+ decryptionError2 = err;
+ }
+ expect(decryptionError2).toMatch('Error');
+ expect(encryptedData2).toBeUndefined();
+ });
+
+ it('should save metadata', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ const originalString = 'abcdefghi';
+ const metadata = { hello: 'world' };
+ await gfsAdapter.createFile('myFileName', originalString, null, {
+ metadata,
+ });
+ const gfsResult = await gfsAdapter.getFileData('myFileName');
+ expect(gfsResult.toString('utf8')).toBe(originalString);
+ let gfsMetadata = await gfsAdapter.getMetadata('myFileName');
+ expect(gfsMetadata.metadata).toEqual(metadata);
+
+ // Empty json for file not found
+ gfsMetadata = await gfsAdapter.getMetadata('myUnknownFile');
+ expect(gfsMetadata).toEqual({});
+ });
+
+ it('should save metadata with file', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ await reconfigureServer({ filesAdapter: gfsAdapter });
+ const str = 'Hello World!';
+ const data = [];
+ for (let i = 0; i < str.length; i++) {
+ data.push(str.charCodeAt(i));
+ }
+ const metadata = { foo: 'bar' };
+ const file = new Parse.File('hello.txt', data, 'text/plain');
+ file.addMetadata('foo', 'bar');
+ await file.save();
+ let fileData = await gfsAdapter.getMetadata(file.name());
+ expect(fileData.metadata).toEqual(metadata);
+
+ // Can only add metadata on create
+ file.addMetadata('hello', 'world');
+ await file.save();
+ fileData = await gfsAdapter.getMetadata(file.name());
+ expect(fileData.metadata).toEqual(metadata);
+
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+ const response = await request({
+ method: 'GET',
+ headers,
+ url: `http://localhost:8378/1/files/test/metadata/${file.name()}`,
+ });
+ fileData = response.data;
+ expect(fileData.metadata).toEqual(metadata);
+ });
+
+ it('should handle getMetadata error', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ await reconfigureServer({ filesAdapter: gfsAdapter });
+ gfsAdapter.getMetadata = () => Promise.reject();
+
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+ const response = await request({
+ method: 'GET',
+ headers,
+ url: `http://localhost:8378/1/files/test/metadata/filename.txt`,
+ });
+ expect(response.data).toEqual({});
+ });
+
+ it('properly fetches a large file from GridFS', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ const twoMegabytesFile = randomString(2048 * 1024);
+ await gfsAdapter.createFile('myFileName', twoMegabytesFile);
+ const gfsResult = await gfsAdapter.getFileData('myFileName');
+ expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
+ });
+
+ it('properly upload a file when disableIndexFieldValidation exist in databaseOptions', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI, { disableIndexFieldValidation: true });
+ const twoMegabytesFile = randomString(2048 * 1024);
+ await gfsAdapter.createFile('myFileName', twoMegabytesFile);
+ const gfsResult = await gfsAdapter.getFileData('myFileName');
+ expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
+ });
+
+ it('properly deletes a file from GridFS', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ await gfsAdapter.createFile('myFileName', 'a simple file');
+ await gfsAdapter.deleteFile('myFileName');
+ await expectMissingFile(gfsAdapter, 'myFileName');
+ }, 1000000);
+
+ it('properly overrides files', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ await gfsAdapter.createFile('myFileName', 'a simple file');
+ await gfsAdapter.createFile('myFileName', 'an overrided simple file');
+ const data = await gfsAdapter.getFileData('myFileName');
+ expect(data.toString('utf8')).toBe('an overrided simple file');
+ const bucket = await gfsAdapter._getBucket();
+ const documents = await bucket.find({ filename: 'myFileName' }).toArray();
+ expect(documents.length).toBe(2);
+ await gfsAdapter.deleteFile('myFileName');
+ await expectMissingFile(gfsAdapter, 'myFileName');
+ });
+
+ it('handleShutdown, close connection', async () => {
+ const databaseURI = 'mongodb://localhost:27017/parse';
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+
+ const db = await gfsAdapter._connect();
+ const status = await db.admin().serverStatus();
+ expect(status.connections.current > 0).toEqual(true);
+
+ await gfsAdapter.handleShutdown();
+ try {
+ await db.admin().serverStatus();
+ expect(false).toBe(true);
+ } catch (e) {
+ expect(e.message).toEqual('Client must be connected before running operations');
+ }
+ });
+
+ it('reports supportsStreaming as true', () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ expect(gfsAdapter.supportsStreaming).toBe(true);
+ });
+
+ it('creates file from Readable stream', async () => {
+ const { Readable } = require('stream');
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ const data = Buffer.from('streamed file content');
+ const stream = Readable.from(data);
+ await gfsAdapter.createFile('streamFile.txt', stream);
+ const result = await gfsAdapter.getFileData('streamFile.txt');
+ expect(result.toString('utf8')).toBe('streamed file content');
+ });
+
+ it('creates encrypted file from Readable stream (buffers for encryption)', async () => {
+ const { Readable } = require('stream');
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI, {}, 'test-encryption-key');
+ const data = Buffer.from('encrypted streamed content');
+ const stream = Readable.from(data);
+ await gfsAdapter.createFile('encryptedStream.txt', stream);
+ const result = await gfsAdapter.getFileData('encryptedStream.txt');
+ expect(result.toString('utf8')).toBe('encrypted streamed content');
+ });
+
+ describe('MongoDB Client Metadata', () => {
+ it('should not pass metadata to MongoClient by default', async () => {
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI);
+ await gfsAdapter._connect();
+ const driverInfo = gfsAdapter._client.s.options.driverInfo;
+ // Either driverInfo should be undefined, or it should not contain our custom metadata
+ if (driverInfo) {
+ expect(driverInfo.name).toBeUndefined();
+ }
+ await gfsAdapter.handleShutdown();
+ });
+
+ it('should pass custom metadata to MongoClient when configured', async () => {
+ const customMetadata = { name: 'MyParseServer', version: '1.0.0' };
+ const gfsAdapter = new GridFSBucketAdapter(databaseURI, {
+ clientMetadata: customMetadata
+ });
+ await gfsAdapter._connect();
+ expect(gfsAdapter._client.s.options.driverInfo.name).toBe(customMetadata.name);
+ expect(gfsAdapter._client.s.options.driverInfo.version).toBe(customMetadata.version);
+ await gfsAdapter.handleShutdown();
+ });
+ });
+});
diff --git a/spec/GridStoreAdapter.js b/spec/GridStoreAdapter.js
deleted file mode 100644
index 78c848f33b..0000000000
--- a/spec/GridStoreAdapter.js
+++ /dev/null
@@ -1,87 +0,0 @@
-var MongoClient = require("mongodb").MongoClient;
-var GridStore = require("mongodb").GridStore;
-
-var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
-var Config = require("../src/Config");
-var FilesController = require('../src/Controllers/FilesController').default;
-
-
-// Small additional tests to improve overall coverage
-describe("GridStoreAdapter",() =>{
- it("should properly instanciate the GridStore when deleting a file", (done) => {
-
- var databaseURI = 'mongodb://localhost:27017/parse';
- var config = new Config(Parse.applicationId);
- var gridStoreAdapter = new GridStoreAdapter(databaseURI);
- var filesController = new FilesController(gridStoreAdapter);
-
- // save original unlink before redefinition
- var originalUnlink = GridStore.prototype.unlink;
-
- var gridStoreMode;
-
- // new unlink method that will capture the mode in which GridStore was opened
- GridStore.prototype.unlink = function() {
-
- // restore original unlink during first call
- GridStore.prototype.unlink = originalUnlink;
-
- gridStoreMode = this.mode;
-
- return originalUnlink.call(this);
- };
-
-
- filesController.createFile(config, 'myFilename.txt', 'my file content', 'text/plain')
- .then(myFile => {
-
- return MongoClient.connect(databaseURI)
- .then(database => {
-
- // Verify the existance of the fs.files document
- return database.collection('fs.files').count().then(count => {
- expect(count).toEqual(1);
- return database;
- });
- })
- .then(database => {
-
- // Verify the existance of the fs.files document
- return database.collection('fs.chunks').count().then(count => {
- expect(count).toEqual(1);
- return database.close();
- });
- })
- .then(() => {
- return filesController.deleteFile(config, myFile.name);
- });
- })
- .then(() => {
- return MongoClient.connect(databaseURI)
- .then(database => {
-
- // Verify the existance of the fs.files document
- return database.collection('fs.files').count().then(count => {
- expect(count).toEqual(0);
- return database;
- });
- })
- .then(database => {
-
- // Verify the existance of the fs.files document
- return database.collection('fs.chunks').count().then(count => {
- expect(count).toEqual(0);
- return database.close();
- });
- });
- })
- .then(() => {
- // Verify that gridStore was opened in read only mode
- expect(gridStoreMode).toEqual('r');
-
- done();
- })
- .catch(fail);
-
- })
-});
diff --git a/spec/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js
index cb34fe70bb..b138a010b0 100644
--- a/spec/HTTPRequest.spec.js
+++ b/spec/HTTPRequest.spec.js
@@ -1,253 +1,189 @@
'use strict';
-var httpRequest = require("../src/cloud-code/httpRequest"),
- HTTPResponse = require('../src/cloud-code/HTTPResponse').default,
- bodyParser = require('body-parser'),
- express = require("express");
-
-var port = 13371;
-var httpRequestServer = "http://localhost:"+port;
-
-var app = express();
-app.use(bodyParser.json({ 'type': '*/*' }));
-app.get("/hello", function(req, res){
- res.json({response: "OK"});
-});
-
-app.get("/404", function(req, res){
- res.status(404);
- res.send("NO");
-});
+const httpRequest = require('../lib/request'),
+ HTTPResponse = require('../lib/request').HTTPResponse,
+ express = require('express');
+
+const port = 13371;
+const httpRequestServer = `http://localhost:${port}`;
+
+function startServer(done) {
+ const app = express();
+ app.use(express.json({ type: '*/*' }));
+ app.get('/hello', function (req, res) {
+ res.json({ response: 'OK' });
+ });
-app.get("/301", function(req, res){
- res.status(301);
- res.location("/hello");
- res.send();
-});
+ app.get('/404', function (req, res) {
+ res.status(404);
+ res.send('NO');
+ });
-app.post('/echo', function(req, res){
- res.json(req.body);
-});
+ app.get('/301', function (req, res) {
+ res.status(301);
+ res.location('/hello');
+ res.send();
+ });
-app.get('/qs', function(req, res){
- res.json(req.query);
-});
+ app.post('/echo', function (req, res) {
+ res.json(req.body);
+ });
-app.listen(13371);
+ app.get('/qs', function (req, res) {
+ res.json(req.query);
+ });
+ return app.listen(13371, undefined, done);
+}
-describe("httpRequest", () => {
- it("should do /hello", (done) => {
- httpRequest({
- url: httpRequestServer+"/hello"
- }).then(function(httpResponse){
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
- expect(httpResponse.text).toEqual('{"response":"OK"}');
- expect(httpResponse.data.response).toEqual("OK");
- done();
- }, function(){
- fail("should not fail");
+describe('httpRequest', () => {
+ let server;
+ beforeEach(done => {
+ if (!server) {
+ server = startServer(done);
+ } else {
done();
- })
+ }
});
- it("should do /hello with callback and promises", (done) => {
- var calls = 0;
- httpRequest({
- url: httpRequestServer+"/hello",
- success: function() { calls++; },
- error: function() { calls++; }
- }).then(function(httpResponse){
- expect(calls).toBe(1);
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
- expect(httpResponse.text).toEqual('{"response":"OK"}');
- expect(httpResponse.data.response).toEqual("OK");
- done();
- }, function(){
- fail("should not fail");
- done();
- })
+ afterAll(done => {
+ server.close(done);
});
- it("should do not follow redirects by default", (done) => {
+ it('should do /hello', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/hello`,
+ });
- httpRequest({
- url: httpRequestServer+"/301"
- }).then(function(httpResponse){
- expect(httpResponse.status).toBe(301);
- done();
- }, function(){
- fail("should not fail");
- done();
- })
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}'));
+ expect(httpResponse.text).toEqual('{"response":"OK"}');
+ expect(httpResponse.data.response).toEqual('OK');
});
- it("should follow redirects when set", (done) => {
+ it('should do not follow redirects by default', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/301`,
+ });
- httpRequest({
- url: httpRequestServer+"/301",
- followRedirects: true
- }).then(function(httpResponse){
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
- expect(httpResponse.text).toEqual('{"response":"OK"}');
- expect(httpResponse.data.response).toEqual("OK");
- done();
- }, function(){
- fail("should not fail");
- done();
- })
+ expect(httpResponse.status).toBe(301);
});
- it("should fail on 404", (done) => {
- var calls = 0;
- httpRequest({
- url: httpRequestServer+"/404",
- success: function() {
- calls++;
- fail("should not succeed");
- done();
- },
- error: function(httpResponse) {
- calls++;
- expect(calls).toBe(1);
- expect(httpResponse.status).toBe(404);
- expect(httpResponse.buffer).toEqual(new Buffer('NO'));
- expect(httpResponse.text).toEqual('NO');
- expect(httpResponse.data).toBe(undefined);
- done();
- }
+ it('should follow redirects when set', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/301`,
+ followRedirects: true,
});
- })
- it("should fail on 404", (done) => {
- httpRequest({
- url: httpRequestServer+"/404",
- }).then(function(httpResponse){
- fail("should not succeed");
- done();
- }, function(httpResponse){
- expect(httpResponse.status).toBe(404);
- expect(httpResponse.buffer).toEqual(new Buffer('NO'));
- expect(httpResponse.text).toEqual('NO');
- expect(httpResponse.data).toBe(undefined);
- done();
- })
- })
-
- it("should post on echo", (done) => {
- var calls = 0;
- httpRequest({
- method: "POST",
- url: httpRequestServer+"/echo",
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}'));
+ expect(httpResponse.text).toEqual('{"response":"OK"}');
+ expect(httpResponse.data.response).toEqual('OK');
+ });
+
+ it('should fail on 404', async () => {
+ await expectAsync(
+ httpRequest({
+ url: `${httpRequestServer}/404`,
+ })
+ ).toBeRejectedWith(
+ jasmine.objectContaining({
+ status: 404,
+ buffer: Buffer.from('NO'),
+ text: 'NO',
+ data: undefined,
+ })
+ );
+ });
+
+ it('should post on echo', async () => {
+ const httpResponse = await httpRequest({
+ method: 'POST',
+ url: `${httpRequestServer}/echo`,
body: {
- foo: "bar"
+ foo: 'bar',
},
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
- success: function() { calls++; },
- error: function() { calls++; }
- }).then(function(httpResponse){
- expect(calls).toBe(1);
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.data).toEqual({foo: "bar"});
- done();
- }, function(httpResponse){
- fail("should not fail");
- done();
- })
+ });
+
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.data).toEqual({ foo: 'bar' });
});
- it("should encode a query string body by default", (done) => {
- let options = {
- body: {"foo": "bar"},
- }
- let result = httpRequest.encodeBody(options);
+ it('should encode a query string body by default', () => {
+ const options = {
+ body: { foo: 'bar' },
+ };
+ const result = httpRequest.encodeBody(options);
+
expect(result.body).toEqual('foo=bar');
expect(result.headers['Content-Type']).toEqual('application/x-www-form-urlencoded');
- done();
+ });
- })
+ it('should encode a JSON body', () => {
+ const options = {
+ body: { foo: 'bar' },
+ headers: { 'Content-Type': 'application/json' },
+ };
+ const result = httpRequest.encodeBody(options);
- it("should encode a JSON body", (done) => {
- let options = {
- body: {"foo": "bar"},
- headers: {'Content-Type': 'application/json'}
- }
- let result = httpRequest.encodeBody(options);
expect(result.body).toEqual('{"foo":"bar"}');
- done();
+ });
- })
- it("should encode a www-form body", (done) => {
- let options = {
- body: {"foo": "bar", "bar": "baz"},
- headers: {'cOntent-tYpe': 'application/x-www-form-urlencoded'}
- }
- let result = httpRequest.encodeBody(options);
- expect(result.body).toEqual("foo=bar&bar=baz");
- done();
+ it('should encode a www-form body', () => {
+ const options = {
+ body: { foo: 'bar', bar: 'baz' },
+ headers: { 'cOntent-tYpe': 'application/x-www-form-urlencoded' },
+ };
+ const result = httpRequest.encodeBody(options);
+
+ expect(result.body).toEqual('foo=bar&bar=baz');
});
- it("should not encode a wrong content type", (done) => {
- let options = {
- body:{"foo": "bar", "bar": "baz"},
- headers: {'cOntent-tYpe': 'mime/jpeg'}
- }
- let result = httpRequest.encodeBody(options);
- expect(result.body).toEqual({"foo": "bar", "bar": "baz"});
- done();
+
+ it('should not encode a wrong content type', () => {
+ const options = {
+ body: { foo: 'bar', bar: 'baz' },
+ headers: { 'cOntent-tYpe': 'mime/jpeg' },
+ };
+ const result = httpRequest.encodeBody(options);
+
+ expect(result.body).toEqual({ foo: 'bar', bar: 'baz' });
});
- it("should fail gracefully", (done) => {
- httpRequest({
- url: "http://not a good url",
- success: function() {
- fail("should not succeed");
- done();
- },
- error: function(error) {
- expect(error).not.toBeUndefined();
- expect(error).not.toBeNull();
- done();
- }
- });
+ it('should fail gracefully', async () => {
+ await expectAsync(
+ httpRequest({
+ url: 'http://not a good url',
+ })
+ ).toBeRejected();
});
- it("should params object to query string", (done) => {
- httpRequest({
- url: httpRequestServer+"/qs",
+ it('should params object to query string', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/qs`,
params: {
- foo: "bar"
- }
- }).then(function(httpResponse){
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.data).toEqual({foo: "bar"});
- done();
- }, function(){
- fail("should not fail");
- done();
- })
+ foo: 'bar',
+ },
+ });
+
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.data).toEqual({ foo: 'bar' });
});
- it("should params string to query string", (done) => {
- httpRequest({
- url: httpRequestServer+"/qs",
- params: "foo=bar&foo2=bar2"
- }).then(function(httpResponse){
- expect(httpResponse.status).toBe(200);
- expect(httpResponse.data).toEqual({foo: "bar", foo2: 'bar2'});
- done();
- }, function(){
- fail("should not fail");
- done();
- })
+ it('should params string to query string', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/qs`,
+ params: 'foo=bar&foo2=bar2',
+ });
+
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.data).toEqual({ foo: 'bar', foo2: 'bar2' });
});
it('should not crash with undefined body', () => {
- let httpResponse = new HTTPResponse({});
+ const httpResponse = new HTTPResponse({});
expect(httpResponse.body).toBeUndefined();
expect(httpResponse.data).toBeUndefined();
expect(httpResponse.text).toBeUndefined();
@@ -255,72 +191,78 @@ describe("httpRequest", () => {
});
it('serialized httpResponse correctly with body string', () => {
- let httpResponse = new HTTPResponse({}, 'hello');
+ const httpResponse = new HTTPResponse({}, 'hello');
expect(httpResponse.text).toBe('hello');
expect(httpResponse.data).toBe(undefined);
expect(httpResponse.body).toBe('hello');
- let serialized = JSON.stringify(httpResponse);
- let result = JSON.parse(serialized);
+ const serialized = JSON.stringify(httpResponse);
+ const result = JSON.parse(serialized);
+
expect(result.text).toBe('hello');
expect(result.data).toBe(undefined);
expect(result.body).toBe(undefined);
});
it('serialized httpResponse correctly with body object', () => {
- let httpResponse = new HTTPResponse({}, {foo: "bar"});
- let encodedResponse = Parse._encode(httpResponse);
- let serialized = JSON.stringify(httpResponse);
- let result = JSON.parse(serialized);
-
+ const httpResponse = new HTTPResponse({}, { foo: 'bar' });
+ Parse._encode(httpResponse);
+ const serialized = JSON.stringify(httpResponse);
+ const result = JSON.parse(serialized);
+
expect(httpResponse.text).toEqual('{"foo":"bar"}');
- expect(httpResponse.data).toEqual({foo: 'bar'});
- expect(httpResponse.body).toEqual({foo: 'bar'});
+ expect(httpResponse.data).toEqual({ foo: 'bar' });
+ expect(httpResponse.body).toEqual({ foo: 'bar' });
expect(result.text).toEqual('{"foo":"bar"}');
- expect(result.data).toEqual({foo: 'bar'});
+ expect(result.data).toEqual({ foo: 'bar' });
expect(result.body).toEqual(undefined);
});
it('serialized httpResponse correctly with body buffer string', () => {
- let httpResponse = new HTTPResponse({}, new Buffer('hello'));
+ const httpResponse = new HTTPResponse({}, Buffer.from('hello'));
expect(httpResponse.text).toBe('hello');
expect(httpResponse.data).toBe(undefined);
- let serialized = JSON.stringify(httpResponse);
- let result = JSON.parse(serialized);
+ const serialized = JSON.stringify(httpResponse);
+ const result = JSON.parse(serialized);
+
expect(result.text).toBe('hello');
expect(result.data).toBe(undefined);
});
it('serialized httpResponse correctly with body buffer JSON Object', () => {
- let json = '{"foo":"bar"}';
- let httpResponse = new HTTPResponse({}, new Buffer(json));
- let serialized = JSON.stringify(httpResponse);
- let result = JSON.parse(serialized);
+ const json = '{"foo":"bar"}';
+ const httpResponse = new HTTPResponse({}, Buffer.from(json));
+ const serialized = JSON.stringify(httpResponse);
+ const result = JSON.parse(serialized);
+
expect(result.text).toEqual('{"foo":"bar"}');
- expect(result.data).toEqual({foo: 'bar'});
+ expect(result.data).toEqual({ foo: 'bar' });
});
it('serialized httpResponse with Parse._encode should be allright', () => {
- let json = '{"foo":"bar"}';
- let httpResponse = new HTTPResponse({}, new Buffer(json));
- let encoded = Parse._encode(httpResponse);
- let foundData, foundText, foundBody = false;
- for(var key in encoded) {
- if (key == 'data') {
+ const json = '{"foo":"bar"}';
+ const httpResponse = new HTTPResponse({}, Buffer.from(json));
+ const encoded = Parse._encode(httpResponse);
+ let foundData,
+ foundText,
+ foundBody = false;
+
+ for (const key in encoded) {
+ if (key === 'data') {
foundData = true;
}
- if (key == 'text') {
+ if (key === 'text') {
foundText = true;
}
- if (key == 'body') {
+ if (key === 'body') {
foundBody = true;
}
}
+
expect(foundData).toBe(true);
expect(foundText).toBe(true);
expect(foundBody).toBe(false);
});
-
});
diff --git a/spec/Idempotency.spec.js b/spec/Idempotency.spec.js
new file mode 100644
index 0000000000..22c00a5e79
--- /dev/null
+++ b/spec/Idempotency.spec.js
@@ -0,0 +1,277 @@
+'use strict';
+const Config = require('../lib/Config');
+const Definitions = require('../lib/Options/Definitions');
+const request = require('../lib/request');
+const rest = require('../lib/rest');
+const auth = require('../lib/Auth');
+const { randomUUID: uuidv4 } = require('crypto');
+
+describe('Idempotency', () => {
+ // Parameters
+ /** Enable TTL expiration simulated by removing entry instead of waiting for MongoDB TTL monitor which
+ runs only every 60s, so it can take up to 119s until entry removal - ain't nobody got time for that */
+ const SIMULATE_TTL = true;
+ const ttl = 2;
+ const maxTimeOut = 4000;
+
+ // Helpers
+ async function deleteRequestEntry(reqId) {
+ const config = Config.get(Parse.applicationId);
+ const res = await rest.find(
+ config,
+ auth.master(config),
+ '_Idempotency',
+ { reqId: reqId },
+ { limit: 1 }
+ );
+ await rest.del(config, auth.master(config), '_Idempotency', res.results[0].objectId);
+ }
+ async function setup(options) {
+ await reconfigureServer({
+ appId: Parse.applicationId,
+ masterKey: Parse.masterKey,
+ serverURL: Parse.serverURL,
+ idempotencyOptions: options,
+ });
+ }
+ // Setups
+ beforeEach(async () => {
+ if (SIMULATE_TTL) {
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
+ }
+ await setup({
+ paths: ['functions/.*', 'jobs/.*', 'classes/.*', 'users', 'installations'],
+ ttl: ttl,
+ });
+ });
+
+ afterEach(() => {
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
+ });
+
+ // Tests
+ it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)('should enforce idempotency for cloud code function', async () => {
+ let counter = 0;
+ Parse.Cloud.define('myFunction', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/myFunction',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl);
+ await request(params);
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('Duplicate request');
+ });
+ expect(counter).toBe(1);
+ });
+
+ it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)('should delete request entry after TTL', async () => {
+ let counter = 0;
+ Parse.Cloud.define('myFunction', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/myFunction',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ if (SIMULATE_TTL) {
+ await deleteRequestEntry('abc-123');
+ } else {
+ await new Promise(resolve => setTimeout(resolve, maxTimeOut));
+ }
+ await expectAsync(request(params)).toBeResolved();
+ expect(counter).toBe(2);
+ });
+
+ it_only_db('postgres')(
+ 'should delete request entry when postgress ttl function is called',
+ async () => {
+ const client = Config.get(Parse.applicationId).database.adapter._client;
+ let counter = 0;
+ Parse.Cloud.define('myFunction', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/myFunction',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ await expectAsync(request(params)).toBeRejected();
+ await new Promise(resolve => setTimeout(resolve, maxTimeOut));
+ await client.one('SELECT idempotency_delete_expired_records()');
+ await expectAsync(request(params)).toBeResolved();
+ expect(counter).toBe(2);
+ }
+ );
+
+ it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)('should enforce idempotency for cloud code jobs', async () => {
+ let counter = 0;
+ Parse.Cloud.job('myJob', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/jobs/myJob',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('Duplicate request');
+ });
+ expect(counter).toBe(1);
+ });
+
+ it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)('should enforce idempotency for class object creation', async () => {
+ let counter = 0;
+ Parse.Cloud.afterSave('MyClass', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/MyClass',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('Duplicate request');
+ });
+ expect(counter).toBe(1);
+ });
+
+ it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)('should enforce idempotency for user object creation', async () => {
+ let counter = 0;
+ Parse.Cloud.afterSave('_User', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/users',
+ body: {
+ username: 'user',
+ password: 'pass',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('Duplicate request');
+ });
+ expect(counter).toBe(1);
+ });
+
+ it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)('should enforce idempotency for installation object creation', async () => {
+ let counter = 0;
+ Parse.Cloud.afterSave('_Installation', () => {
+ counter++;
+ });
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/installations',
+ body: {
+ installationId: '1',
+ deviceType: 'ios',
+ },
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await expectAsync(request(params)).toBeResolved();
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('Duplicate request');
+ });
+ expect(counter).toBe(1);
+ });
+
+ it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)('should not interfere with calls of different request ID', async () => {
+ let counter = 0;
+ Parse.Cloud.afterSave('MyClass', () => {
+ counter++;
+ });
+ const promises = [...Array(100).keys()].map(() => {
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/MyClass',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': uuidv4(),
+ },
+ };
+ return request(params);
+ });
+ await expectAsync(Promise.all(promises)).toBeResolved();
+ expect(counter).toBe(100);
+ });
+
+ it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => {
+ spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error'));
+ Parse.Cloud.define('myFunction', () => {});
+ const params = {
+ method: 'POST',
+ url: 'http://localhost:8378/1/functions/myFunction',
+ headers: {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Master-Key': Parse.masterKey,
+ 'X-Parse-Request-Id': 'abc-123',
+ },
+ };
+ await request(params).then(fail, e => {
+ expect(e.status).toEqual(400);
+ expect(e.data.error).toEqual('some other error');
+ });
+ });
+
+ it('should use default configuration when none is set', async () => {
+ await setup({});
+ expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(
+ Definitions.IdempotencyOptions.ttl.default
+ );
+ expect(Config.get(Parse.applicationId).idempotencyOptions.paths).toBe(
+ Definitions.IdempotencyOptions.paths.default
+ );
+ });
+
+ it('should throw on invalid configuration', async () => {
+ await expectAsync(setup({ paths: 1 })).toBeRejected();
+ await expectAsync(setup({ ttl: 'a' })).toBeRejected();
+ await expectAsync(setup({ ttl: 0 })).toBeRejected();
+ await expectAsync(setup({ ttl: -1 })).toBeRejected();
+ });
+});
diff --git a/spec/InMemoryCache.spec.js b/spec/InMemoryCache.spec.js
index 3c0fb47bbb..4a474b7fa2 100644
--- a/spec/InMemoryCache.spec.js
+++ b/spec/InMemoryCache.spec.js
@@ -1,42 +1,40 @@
-const InMemoryCache = require('../src/Adapters/Cache/InMemoryCache').default;
+const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default;
-
-describe('InMemoryCache', function() {
- var BASE_TTL = {
- ttl: 10
+describe('InMemoryCache', function () {
+ const BASE_TTL = {
+ ttl: 100,
};
- var NO_EXPIRE_TTL = {
- ttl: NaN
+ const NO_EXPIRE_TTL = {
+ ttl: NaN,
};
- var KEY = 'hello';
- var KEY_2 = KEY + '_2';
-
- var VALUE = 'world';
+ const KEY = 'hello';
+ const KEY_2 = KEY + '_2';
+ const VALUE = 'world';
function wait(sleep) {
- return new Promise(function(resolve, reject) {
+ return new Promise(function (resolve) {
setTimeout(resolve, sleep);
- })
+ });
}
- it('should destroy a expire items in the cache', (done) => {
- var cache = new InMemoryCache(BASE_TTL);
+ it('should destroy a expire items in the cache', done => {
+ const cache = new InMemoryCache(BASE_TTL);
cache.put(KEY, VALUE);
- var value = cache.get(KEY);
+ let value = cache.get(KEY);
expect(value).toEqual(VALUE);
- wait(BASE_TTL.ttl * 5).then(() => {
- value = cache.get(KEY)
+ wait(BASE_TTL.ttl * 10).then(() => {
+ value = cache.get(KEY);
expect(value).toEqual(null);
done();
});
});
- it('should delete items', (done) => {
- var cache = new InMemoryCache(NO_EXPIRE_TTL);
+ it('should delete items', done => {
+ const cache = new InMemoryCache(NO_EXPIRE_TTL);
cache.put(KEY, VALUE);
cache.put(KEY_2, VALUE);
expect(cache.get(KEY)).toEqual(VALUE);
@@ -52,8 +50,8 @@ describe('InMemoryCache', function() {
done();
});
- it('should clear all items', (done) => {
- var cache = new InMemoryCache(NO_EXPIRE_TTL);
+ it('should clear all items', done => {
+ const cache = new InMemoryCache(NO_EXPIRE_TTL);
cache.put(KEY, VALUE);
cache.put(KEY_2, VALUE);
@@ -67,8 +65,7 @@ describe('InMemoryCache', function() {
});
it('should deafult TTL to 5 seconds', () => {
- var cache = new InMemoryCache({});
+ const cache = new InMemoryCache({});
expect(cache.ttl).toEqual(5 * 1000);
});
-
});
diff --git a/spec/InMemoryCacheAdapter.spec.js b/spec/InMemoryCacheAdapter.spec.js
index 405da6f7ad..add976fbc9 100644
--- a/spec/InMemoryCacheAdapter.spec.js
+++ b/spec/InMemoryCacheAdapter.spec.js
@@ -1,59 +1,53 @@
-var InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').default;
+const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').default;
-describe('InMemoryCacheAdapter', function() {
- var KEY = 'hello';
- var VALUE = 'world';
+describe('InMemoryCacheAdapter', function () {
+ const KEY = 'hello';
+ const VALUE = 'world';
function wait(sleep) {
- return new Promise(function(resolve, reject) {
+ return new Promise(function (resolve) {
setTimeout(resolve, sleep);
- })
+ });
}
- it('should expose promisifyed methods', (done) => {
- var cache = new InMemoryCacheAdapter({
- ttl: NaN
+ it('should expose promisifyed methods', done => {
+ const cache = new InMemoryCacheAdapter({
+ ttl: NaN,
});
- var noop = () => {};
-
// Verify all methods return promises.
- Promise.all([
- cache.put(KEY, VALUE),
- cache.del(KEY),
- cache.get(KEY),
- cache.clear()
- ]).then(() => {
+ Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => {
done();
});
});
- it('should get/set/clear', (done) => {
- var cache = new InMemoryCacheAdapter({
- ttl: NaN
+ it('should get/set/clear', done => {
+ const cache = new InMemoryCacheAdapter({
+ ttl: NaN,
});
- cache.put(KEY, VALUE)
+ cache
+ .put(KEY, VALUE)
.then(() => cache.get(KEY))
- .then((value) => expect(value).toEqual(VALUE))
+ .then(value => expect(value).toEqual(VALUE))
.then(() => cache.clear())
.then(() => cache.get(KEY))
- .then((value) => expect(value).toEqual(null))
+ .then(value => expect(value).toEqual(null))
.then(done);
});
- it('should expire after ttl', (done) => {
- var cache = new InMemoryCacheAdapter({
- ttl: 10
+ it('should expire after ttl', done => {
+ const cache = new InMemoryCacheAdapter({
+ ttl: 10,
});
- cache.put(KEY, VALUE)
+ cache
+ .put(KEY, VALUE)
.then(() => cache.get(KEY))
- .then((value) => expect(value).toEqual(VALUE))
+ .then(value => expect(value).toEqual(VALUE))
.then(wait.bind(null, 50))
.then(() => cache.get(KEY))
- .then((value) => expect(value).toEqual(null))
+ .then(value => expect(value).toEqual(null))
.then(done);
- })
-
+ });
});
diff --git a/spec/InstallationDedup.spec.js b/spec/InstallationDedup.spec.js
new file mode 100644
index 0000000000..c73c574cf6
--- /dev/null
+++ b/spec/InstallationDedup.spec.js
@@ -0,0 +1,319 @@
+'use strict';
+
+const Parse = require('parse/node').Parse;
+
+describe('InstallationDedup', () => {
+ let InstallationDedup;
+ let logger;
+ let logSpy;
+
+ beforeEach(() => {
+ InstallationDedup = require('../lib/InstallationDedup');
+ logger = require('../lib/logger').logger;
+ logSpy = {
+ verbose: spyOn(logger, 'verbose').and.callFake(() => {}),
+ warn: spyOn(logger, 'warn').and.callFake(() => {}),
+ error: spyOn(logger, 'error').and.callFake(() => {}),
+ };
+ });
+
+ describe('removeConflictingDeviceToken', () => {
+ it('action="delete" with no match resolves silently and logs verbose', async () => {
+ const database = {
+ destroy: jasmine
+ .createSpy('destroy')
+ .and.returnValue(Promise.reject({ code: Parse.Error.OBJECT_NOT_FOUND })),
+ };
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'delete',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(database.destroy).toHaveBeenCalled();
+ expect(logSpy.verbose).toHaveBeenCalled();
+ expect(logSpy.warn).not.toHaveBeenCalled();
+ expect(logSpy.error).not.toHaveBeenCalled();
+ });
+
+ it('action="delete" with matches calls destroy with empty options when enforceAuth=false', async () => {
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ };
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'delete',
+ enforceAuth: false,
+ runOptions: { acl: ['*'] },
+ validSchemaController: undefined,
+ });
+ expect(database.destroy).toHaveBeenCalledWith(
+ '_Installation',
+ { deviceToken: 'X' },
+ {},
+ undefined
+ );
+ expect(logSpy.verbose).toHaveBeenCalled();
+ });
+
+ it('action="delete" with enforceAuth=true passes runOptions to destroy', async () => {
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ };
+ const runOptions = { acl: ['*', 'userABC'] };
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'delete',
+ enforceAuth: true,
+ runOptions,
+ validSchemaController: undefined,
+ });
+ expect(database.destroy).toHaveBeenCalledWith(
+ '_Installation',
+ { deviceToken: 'X' },
+ runOptions,
+ undefined
+ );
+ });
+
+ it('action="update" calls update with deviceToken cleared and many=true in options', async () => {
+ const database = {
+ update: jasmine.createSpy('update').and.returnValue(Promise.resolve()),
+ };
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'update',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(database.update).toHaveBeenCalledWith(
+ '_Installation',
+ { deviceToken: 'X' },
+ { deviceToken: { __op: 'Delete' } },
+ jasmine.objectContaining({ many: true }),
+ false,
+ false,
+ undefined
+ );
+ expect(logSpy.verbose).toHaveBeenCalled();
+ });
+
+ it('OPERATION_FORBIDDEN error is swallowed and logged as warn', async () => {
+ const database = {
+ destroy: jasmine
+ .createSpy('destroy')
+ .and.returnValue(
+ Promise.reject({ code: Parse.Error.OPERATION_FORBIDDEN, message: 'denied' })
+ ),
+ };
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'delete',
+ enforceAuth: true,
+ runOptions: { acl: ['*'] },
+ validSchemaController: undefined,
+ });
+ expect(logSpy.warn).toHaveBeenCalled();
+ expect(logSpy.error).not.toHaveBeenCalled();
+ });
+
+ it('unexpected error is logged as error and rethrown', async () => {
+ const database = {
+ destroy: jasmine
+ .createSpy('destroy')
+ .and.returnValue(Promise.reject(new Error('database connection lost'))),
+ };
+ let caught;
+ try {
+ await InstallationDedup.removeConflictingDeviceToken({
+ database,
+ query: { deviceToken: 'X' },
+ action: 'delete',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ } catch (e) {
+ caught = e;
+ }
+ expect(caught).toBeDefined();
+ expect(caught.message).toBe('database connection lost');
+ expect(logSpy.error).toHaveBeenCalled();
+ });
+ });
+
+ describe('applyDuplicateDeviceTokenMerge', () => {
+ const idMatch = { objectId: 'A', installationId: 'I' };
+ const deviceTokenMatch = { objectId: 'B', deviceToken: 'X' };
+
+ it('mergePriority="deviceToken" + action="delete" destroys idMatch and returns deviceTokenMatch.objectId', async () => {
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'delete',
+ mergePriority: 'deviceToken',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('B');
+ expect(database.destroy).toHaveBeenCalledWith(
+ '_Installation',
+ { objectId: 'A' },
+ {},
+ undefined
+ );
+ expect(logSpy.verbose).toHaveBeenCalled();
+ });
+
+ it('mergePriority="deviceToken" + action="update" clears installationId on idMatch and returns deviceTokenMatch.objectId', async () => {
+ const database = {
+ update: jasmine.createSpy('update').and.returnValue(Promise.resolve()),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'update',
+ mergePriority: 'deviceToken',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('B');
+ expect(database.update).toHaveBeenCalledWith(
+ '_Installation',
+ { objectId: 'A' },
+ { installationId: { __op: 'Delete' } },
+ jasmine.objectContaining({ many: false }),
+ false,
+ false,
+ undefined
+ );
+ });
+
+ it('mergePriority="installationId" + action="delete" destroys deviceTokenMatch and returns idMatch.objectId', async () => {
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'delete',
+ mergePriority: 'installationId',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('A');
+ expect(database.destroy).toHaveBeenCalledWith(
+ '_Installation',
+ { objectId: 'B' },
+ {},
+ undefined
+ );
+ });
+
+ it('mergePriority="installationId" + action="update" clears deviceToken on deviceTokenMatch and returns idMatch.objectId', async () => {
+ const database = {
+ update: jasmine.createSpy('update').and.returnValue(Promise.resolve()),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'update',
+ mergePriority: 'installationId',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('A');
+ expect(database.update).toHaveBeenCalledWith(
+ '_Installation',
+ { objectId: 'B' },
+ { deviceToken: { __op: 'Delete' } },
+ jasmine.objectContaining({ many: false }),
+ false,
+ false,
+ undefined
+ );
+ });
+
+ it('OPERATION_FORBIDDEN on the merge action still returns survivor objectId (silent skip)', async () => {
+ const database = {
+ destroy: jasmine
+ .createSpy('destroy')
+ .and.returnValue(Promise.reject({ code: Parse.Error.OPERATION_FORBIDDEN })),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'delete',
+ mergePriority: 'deviceToken',
+ enforceAuth: true,
+ runOptions: { acl: ['*'] },
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('B');
+ expect(logSpy.warn).toHaveBeenCalled();
+ });
+
+ it('returns the shared objectId without calling destroy/update when idMatch and deviceTokenMatch are the same row', async () => {
+ const sameRow = { objectId: 'SAME', installationId: 'I', deviceToken: 'X' };
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ update: jasmine.createSpy('update').and.returnValue(Promise.resolve()),
+ };
+ const result = await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch: sameRow,
+ deviceTokenMatch: sameRow,
+ action: 'delete',
+ mergePriority: 'deviceToken',
+ enforceAuth: false,
+ runOptions: {},
+ validSchemaController: undefined,
+ });
+ expect(result).toBe('SAME');
+ expect(database.destroy).not.toHaveBeenCalled();
+ expect(database.update).not.toHaveBeenCalled();
+ });
+
+ it('enforceAuth=true passes runOptions to destroy', async () => {
+ const database = {
+ destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
+ };
+ const runOptions = { acl: ['*', 'userABC'] };
+ await InstallationDedup.applyDuplicateDeviceTokenMerge({
+ database,
+ idMatch,
+ deviceTokenMatch,
+ action: 'delete',
+ mergePriority: 'deviceToken',
+ enforceAuth: true,
+ runOptions,
+ validSchemaController: undefined,
+ });
+ expect(database.destroy).toHaveBeenCalledWith(
+ '_Installation',
+ { objectId: 'A' },
+ runOptions,
+ undefined
+ );
+ });
+ });
+});
diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js
index 60965ff967..1ccad5013d 100644
--- a/spec/InstallationsRouter.spec.js
+++ b/spec/InstallationsRouter.spec.js
@@ -1,177 +1,247 @@
-var auth = require('../src/Auth');
-var Config = require('../src/Config');
-var rest = require('../src/rest');
-var InstallationsRouter = require('../src/Routers/InstallationsRouter').InstallationsRouter;
-
-var config = new Config('test');
+const auth = require('../lib/Auth');
+const Config = require('../lib/Config');
+const rest = require('../lib/rest');
+const InstallationsRouter = require('../lib/Routers/InstallationsRouter').InstallationsRouter;
describe('InstallationsRouter', () => {
- it('uses find condition from request.body', (done) => {
- var androidDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abc',
- 'deviceType': 'android'
+ it('uses find condition from request.body', done => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
- var iosDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abd',
- 'deviceType': 'ios'
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
};
- var request = {
+ const request = {
config: config,
auth: auth.master(config),
body: {
where: {
- deviceType: 'android'
- }
+ deviceType: 'android',
+ },
},
query: {},
- info: {}
+ info: {},
};
- var router = new InstallationsRouter();
- rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
- .then(() => {
- return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
- }).then(() => {
- return router.handleFind(request);
- }).then((res) => {
- var results = res.response.results;
- expect(results.length).toEqual(1);
- done();
- });
+ const router = new InstallationsRouter();
+ rest
+ .create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
+ .then(() => {
+ return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const results = res.response.results;
+ expect(results.length).toEqual(1);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
});
- it('uses find condition from request.query', (done) => {
- var androidDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abc',
- 'deviceType': 'android'
+ it('uses find condition from request.query', done => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
- var iosDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abd',
- 'deviceType': 'ios'
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
};
- var request = {
+ const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
where: {
- deviceType: 'android'
- }
+ deviceType: 'android',
+ },
},
- info: {}
+ info: {},
};
- var router = new InstallationsRouter();
- rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
- .then(() => {
- return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
- }).then(() => {
- return router.handleFind(request);
- }).then((res) => {
- var results = res.response.results;
- expect(results.length).toEqual(1);
- done();
- });
+ const router = new InstallationsRouter();
+ rest
+ .create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
+ .then(() => {
+ return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const results = res.response.results;
+ expect(results.length).toEqual(1);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ done();
+ });
});
- it('query installations with limit = 0', (done) => {
- var androidDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abc',
- 'deviceType': 'android'
+ it('query installations with limit = 0', done => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
- var iosDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abd',
- 'deviceType': 'ios'
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
};
- var request = {
+ const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
- limit: 0
+ limit: 0,
},
- info: {}
+ info: {},
};
- var router = new InstallationsRouter();
- rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
- .then(() => {
- return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
- }).then(() => {
- return router.handleFind(request);
- }).then((res) => {
- var response = res.response;
- expect(response.results.length).toEqual(0);
- done();
- });
+ Config.get('test');
+ const router = new InstallationsRouter();
+ rest
+ .create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
+ .then(() => {
+ return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(0);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
});
- it('query installations with count = 1', done => {
- var androidDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abc',
- 'deviceType': 'android'
+ it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
- var iosDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abd',
- 'deviceType': 'ios'
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
};
- var request = {
+ const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
- count: 1
+ count: 1,
},
- info: {}
+ info: {},
+ };
+
+ const router = new InstallationsRouter();
+ rest
+ .create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
+ .then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
+ .then(() => router.handleFind(request))
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(2);
+ expect(response.count).toEqual(2);
+ done();
+ })
+ .catch(error => {
+ fail(JSON.stringify(error));
+ done();
+ });
+ });
+
+ it_only_db('postgres')('query installations with count = 1 postgres', async () => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ count: 1,
+ },
+ info: {},
+ };
+
+ const router = new InstallationsRouter();
+ await rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest);
+ await rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
+ let res = await router.handleFind(request);
+ let response = res.response;
+ expect(response.results.length).toEqual(2);
+ expect(response.count).toEqual(0); // estimate count is zero
+
+ const pgAdapter = config.database.adapter;
+ await pgAdapter.updateEstimatedCount('_Installation');
- var router = new InstallationsRouter();
- rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
- .then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
- .then(() => router.handleFind(request))
- .then((res) => {
- var response = res.response;
- expect(response.results.length).toEqual(2);
- expect(response.count).toEqual(2);
- done();
- })
- .catch(error => {
- fail(JSON.stringify(error));
- done();
- })
+ res = await router.handleFind(request);
+ response = res.response;
+ expect(response.results.length).toEqual(2);
+ expect(response.count).toEqual(2);
});
- it('query installations with limit = 0 and count = 1', (done) => {
- var androidDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abc',
- 'deviceType': 'android'
+ it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => {
+ const config = Config.get('test');
+ const androidDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abc',
+ deviceType: 'android',
};
- var iosDeviceRequest = {
- 'installationId': '12345678-abcd-abcd-abcd-123456789abd',
- 'deviceType': 'ios'
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
};
- var request = {
+ const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0,
- count: 1
+ count: 1,
},
- info: {}
+ info: {},
};
- var router = new InstallationsRouter();
- rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
- .then(() => {
- return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
- }).then(() => {
- return router.handleFind(request);
- }).then((res) => {
- var response = res.response;
- expect(response.results.length).toEqual(0);
- expect(response.count).toEqual(2);
- done();
- });
+ const router = new InstallationsRouter();
+ rest
+ .create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
+ .then(() => {
+ return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
+ })
+ .then(() => {
+ return router.handleFind(request);
+ })
+ .then(res => {
+ const response = res.response;
+ expect(response.results.length).toEqual(0);
+ expect(response.count).toEqual(2);
+ done();
+ })
+ .catch(err => {
+ fail(JSON.stringify(err));
+ done();
+ });
});
});
diff --git a/spec/JobSchedule.spec.js b/spec/JobSchedule.spec.js
new file mode 100644
index 0000000000..853eb20143
--- /dev/null
+++ b/spec/JobSchedule.spec.js
@@ -0,0 +1,272 @@
+const request = require('../lib/request');
+
+const defaultHeaders = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Rest-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+};
+const masterKeyHeaders = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Rest-API-Key': 'rest',
+ 'X-Parse-Master-Key': 'test',
+ 'Content-Type': 'application/json',
+};
+const defaultOptions = {
+ headers: defaultHeaders,
+ json: true,
+};
+const masterKeyOptions = {
+ headers: masterKeyHeaders,
+ json: true,
+};
+
+describe('JobSchedule', () => {
+ it('should create _JobSchedule with masterKey', done => {
+ const jobSchedule = new Parse.Object('_JobSchedule');
+ jobSchedule.set({
+ jobName: 'MY Cool Job',
+ });
+ jobSchedule
+ .save(null, { useMasterKey: true })
+ .then(() => {
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('should fail creating _JobSchedule without masterKey', done => {
+ const jobSchedule = new Parse.Object('_JobSchedule');
+ jobSchedule.set({
+ jobName: 'SomeJob',
+ });
+ jobSchedule
+ .save(null)
+ .then(done.fail)
+ .catch(() => done());
+ });
+
+ it('should reject access when not using masterKey (/jobs)', done => {
+ request(
+ Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions)
+ ).then(done.fail, () => done());
+ });
+
+ it('should reject access when not using masterKey (/jobs/data)', done => {
+ request(
+ Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions)
+ ).then(done.fail, () => done());
+ });
+
+ it('should reject access when not using masterKey (PUT /jobs/id)', done => {
+ request(
+ Object.assign(
+ { method: 'PUT', url: Parse.serverURL + '/cloud_code/jobs/jobId' },
+ defaultOptions
+ )
+ ).then(done.fail, () => done());
+ });
+
+ it('should reject access when not using masterKey (DELETE /jobs/id)', done => {
+ request(
+ Object.assign(
+ { method: 'DELETE', url: Parse.serverURL + '/cloud_code/jobs/jobId' },
+ defaultOptions
+ )
+ ).then(done.fail, () => done());
+ });
+
+ it('should allow access when using masterKey (GET /jobs)', done => {
+ request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions)).then(
+ done,
+ done.fail
+ );
+ });
+
+ it('should create a job schedule', done => {
+ Parse.Cloud.job('job', () => {});
+ const options = Object.assign({}, masterKeyOptions, {
+ method: 'POST',
+ url: Parse.serverURL + '/cloud_code/jobs',
+ body: {
+ job_schedule: {
+ jobName: 'job',
+ },
+ },
+ });
+ request(options)
+ .then(res => {
+ expect(res.data.objectId).not.toBeUndefined();
+ })
+ .then(() => {
+ return request(
+ Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions)
+ );
+ })
+ .then(res => {
+ expect(res.data.length).toBe(1);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should fail creating a job with an invalid name', done => {
+ const options = Object.assign({}, masterKeyOptions, {
+ url: Parse.serverURL + '/cloud_code/jobs',
+ method: 'POST',
+ body: {
+ job_schedule: {
+ jobName: 'job',
+ },
+ },
+ });
+ request(options)
+ .then(done.fail)
+ .catch(() => done());
+ });
+
+ it('should update a job', done => {
+ Parse.Cloud.job('job1', () => {});
+ Parse.Cloud.job('job2', () => {});
+ const options = Object.assign({}, masterKeyOptions, {
+ method: 'POST',
+ url: Parse.serverURL + '/cloud_code/jobs',
+ body: {
+ job_schedule: {
+ jobName: 'job1',
+ },
+ },
+ });
+ request(options)
+ .then(res => {
+ expect(res.data.objectId).not.toBeUndefined();
+ return request(
+ Object.assign(options, {
+ url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
+ method: 'PUT',
+ body: {
+ job_schedule: {
+ jobName: 'job2',
+ },
+ },
+ })
+ );
+ })
+ .then(() => {
+ return request(
+ Object.assign({}, masterKeyOptions, {
+ url: Parse.serverURL + '/cloud_code/jobs',
+ })
+ );
+ })
+ .then(res => {
+ expect(res.data.length).toBe(1);
+ expect(res.data[0].jobName).toBe('job2');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should fail updating a job with an invalid name', done => {
+ Parse.Cloud.job('job1', () => {});
+ const options = Object.assign({}, masterKeyOptions, {
+ method: 'POST',
+ url: Parse.serverURL + '/cloud_code/jobs',
+ body: {
+ job_schedule: {
+ jobName: 'job1',
+ },
+ },
+ });
+ request(options)
+ .then(res => {
+ expect(res.data.objectId).not.toBeUndefined();
+ return request(
+ Object.assign(options, {
+ method: 'PUT',
+ url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
+ body: {
+ job_schedule: {
+ jobName: 'job2',
+ },
+ },
+ })
+ );
+ })
+ .then(done.fail)
+ .catch(() => done());
+ });
+
+ it('should destroy a job', done => {
+ Parse.Cloud.job('job', () => {});
+ const options = Object.assign({}, masterKeyOptions, {
+ method: 'POST',
+ url: Parse.serverURL + '/cloud_code/jobs',
+ body: {
+ job_schedule: {
+ jobName: 'job',
+ },
+ },
+ });
+ request(options)
+ .then(res => {
+ expect(res.data.objectId).not.toBeUndefined();
+ return request(
+ Object.assign(
+ {
+ method: 'DELETE',
+ url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
+ },
+ masterKeyOptions
+ )
+ );
+ })
+ .then(() => {
+ return request(
+ Object.assign(
+ {
+ url: Parse.serverURL + '/cloud_code/jobs',
+ },
+ masterKeyOptions
+ )
+ );
+ })
+ .then(res => {
+ expect(res.data.length).toBe(0);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should properly return job data', done => {
+ Parse.Cloud.job('job1', () => {});
+ Parse.Cloud.job('job2', () => {});
+ const options = Object.assign({}, masterKeyOptions, {
+ method: 'POST',
+ url: Parse.serverURL + '/cloud_code/jobs',
+ body: {
+ job_schedule: {
+ jobName: 'job1',
+ },
+ },
+ });
+ request(options)
+ .then(response => {
+ const res = response.data;
+ expect(res.objectId).not.toBeUndefined();
+ })
+ .then(() => {
+ return request(
+ Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, masterKeyOptions)
+ );
+ })
+ .then(response => {
+ const res = response.data;
+ expect(res.in_use).toEqual(['job1']);
+ expect(res.jobs).toContain('job1');
+ expect(res.jobs).toContain('job2');
+ expect(res.jobs.length).toBe(2);
+ })
+ .then(done)
+ .catch(e => done.fail(e.data));
+ });
+});
diff --git a/spec/LdapAuth.spec.js b/spec/LdapAuth.spec.js
new file mode 100644
index 0000000000..b577defcd9
--- /dev/null
+++ b/spec/LdapAuth.spec.js
@@ -0,0 +1,384 @@
+const ldap = require('../lib/Adapters/Auth/ldap');
+const mockLdapServer = require('./support/MockLdapServer');
+const fs = require('fs');
+const port = 12345;
+const sslport = 12346;
+
+describe('LDAP Injection Prevention', () => {
+ describe('escapeDN', () => {
+ it('should escape comma', () => {
+ expect(ldap.escapeDN('admin,ou=evil')).toBe('admin\\,ou\\=evil');
+ });
+
+ it('should escape equals sign', () => {
+ expect(ldap.escapeDN('admin=evil')).toBe('admin\\=evil');
+ });
+
+ it('should escape plus sign', () => {
+ expect(ldap.escapeDN('admin+evil')).toBe('admin\\+evil');
+ });
+
+ it('should escape less-than and greater-than signs', () => {
+ expect(ldap.escapeDN('admin')).toBe('admin\\');
+ });
+
+ it('should escape hash at start', () => {
+ expect(ldap.escapeDN('#admin')).toBe('\\#admin');
+ });
+
+ it('should escape semicolon', () => {
+ expect(ldap.escapeDN('admin;evil')).toBe('admin\\;evil');
+ });
+
+ it('should escape double quote', () => {
+ expect(ldap.escapeDN('admin"evil')).toBe('admin\\"evil');
+ });
+
+ it('should escape backslash', () => {
+ expect(ldap.escapeDN('admin\\evil')).toBe('admin\\\\evil');
+ });
+
+ it('should escape leading space', () => {
+ expect(ldap.escapeDN(' admin')).toBe('\\ admin');
+ });
+
+ it('should escape trailing space', () => {
+ expect(ldap.escapeDN('admin ')).toBe('admin\\ ');
+ });
+
+ it('should escape multiple special characters', () => {
+ expect(ldap.escapeDN('admin,ou=evil+cn=x')).toBe('admin\\,ou\\=evil\\+cn\\=x');
+ });
+
+ it('should not modify safe values', () => {
+ expect(ldap.escapeDN('testuser')).toBe('testuser');
+ expect(ldap.escapeDN('john.doe')).toBe('john.doe');
+ expect(ldap.escapeDN('user123')).toBe('user123');
+ });
+ });
+
+ describe('escapeFilter', () => {
+ it('should escape asterisk', () => {
+ expect(ldap.escapeFilter('*')).toBe('\\2a');
+ });
+
+ it('should escape open parenthesis', () => {
+ expect(ldap.escapeFilter('test(')).toBe('test\\28');
+ });
+
+ it('should escape close parenthesis', () => {
+ expect(ldap.escapeFilter('test)')).toBe('test\\29');
+ });
+
+ it('should escape backslash', () => {
+ expect(ldap.escapeFilter('test\\')).toBe('test\\5c');
+ });
+
+ it('should escape null byte', () => {
+ expect(ldap.escapeFilter('test\x00')).toBe('test\\00');
+ });
+
+ it('should escape multiple special characters', () => {
+ expect(ldap.escapeFilter('*()\\')).toBe('\\2a\\28\\29\\5c');
+ });
+
+ it('should not modify safe values', () => {
+ expect(ldap.escapeFilter('testuser')).toBe('testuser');
+ expect(ldap.escapeFilter('john.doe')).toBe('john.doe');
+ expect(ldap.escapeFilter('user123')).toBe('user123');
+ });
+
+ it('should escape filter injection attempt with wildcard', () => {
+ expect(ldap.escapeFilter('x)(|(objectClass=*)')).toBe('x\\29\\28|\\28objectClass=\\2a\\29');
+ });
+ });
+
+ describe('authData validation', () => {
+ it('should reject missing authData.id', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ try {
+ await ldap.validateAuthData({ password: 'secret' }, options);
+ fail('Should have rejected missing id');
+ } catch (err) {
+ expect(err.message).toBe('LDAP: Wrong username or password');
+ }
+ server.close(done);
+ });
+
+ it('should reject non-string authData.id', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ try {
+ await ldap.validateAuthData({ id: 123, password: 'secret' }, options);
+ fail('Should have rejected non-string id');
+ } catch (err) {
+ expect(err.message).toBe('LDAP: Wrong username or password');
+ }
+ server.close(done);
+ });
+ });
+
+ describe('DN injection prevention', () => {
+ it('should prevent DN injection via comma in authData.id', async done => {
+ // Mock server accepts the DN that would result from an unescaped injection
+ const server = await mockLdapServer(port, 'uid=admin,ou=admins,o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ // Attacker tries to inject additional DN components via comma
+ // Without escaping: DN = uid=admin,ou=admins, o=example (3 RDNs) â matches mock
+ // With escaping: DN = uid=admin\,ou=admins, o=example (2 RDNs) â doesn't match
+ try {
+ await ldap.validateAuthData({ id: 'admin,ou=admins', password: 'secret' }, options);
+ fail('Should have rejected DN injection attempt');
+ } catch (err) {
+ expect(err.message).toBe('LDAP: Wrong username or password');
+ }
+ server.close(done);
+ });
+ });
+
+ describe('Filter injection prevention', () => {
+ it('should prevent LDAP filter injection via wildcard in authData.id', async done => {
+ // Mock server accepts uid=*, o=example (the attacker's bind DN)
+ // The * is not special in DNs so it binds fine regardless of escaping
+ const server = await mockLdapServer(port, 'uid=*, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ groupCn: 'powerusers',
+ groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
+ };
+ // Attacker uses * as ID to match any group member via wildcard
+ // Group has member uid=testuser, not uid=*
+ // Without escaping: filter uses SubstringFilter, matches testuser â passes
+ // With escaping: filter uses EqualityFilter with literal \2a, no match â fails
+ try {
+ await ldap.validateAuthData({ id: '*', password: 'secret' }, options);
+ fail('Should have rejected filter injection attempt');
+ } catch (err) {
+ expect(err.message).toBe('LDAP: User not in group');
+ }
+ server.close(done);
+ });
+ });
+});
+
+describe('Ldap Auth', () => {
+ it('Should fail with missing options', done => {
+ ldap
+ .validateAuthData({ id: 'testuser', password: 'testpw' })
+ .then(done.fail)
+ .catch(err => {
+ jequal(err.message, 'LDAP auth configuration missing');
+ done();
+ });
+ });
+
+ it('Should return a resolved promise when validating the app id', done => {
+ ldap.validateAppId().then(done).catch(done.fail);
+ });
+
+ it('Should succeed with right credentials', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ server.close(done);
+ });
+
+ it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => {
+ const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldaps://localhost:${sslport}`,
+ dn: 'uid={{id}}, o=example',
+ tlsOptions: { rejectUnauthorized: false },
+ };
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ server.close(done);
+ });
+
+ it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => {
+ const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldaps://localhost:${sslport}`,
+ dn: 'uid={{id}}, o=example',
+ tlsOptions: {
+ ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
+ rejectUnauthorized: true,
+ },
+ };
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ server.close(done);
+ });
+
+ it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => {
+ const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldaps://localhost:${sslport}`,
+ dn: 'uid={{id}}, o=example',
+ tlsOptions: {
+ ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
+ rejectUnauthorized: true,
+ },
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAPS: Certificate mismatch');
+ }
+ server.close(done);
+ });
+
+ it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => {
+ const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldaps://localhost:${sslport}`,
+ dn: 'uid={{id}}, o=example',
+ tlsOptions: {
+ ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
+ rejectUnauthorized: true,
+ },
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAP: Wrong username or password');
+ }
+ server.close(done);
+ });
+
+ it('Should fail with wrong credentials', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAP: Wrong username or password');
+ }
+ server.close(done);
+ });
+
+ it('Should succeed if user is in given group', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ groupCn: 'powerusers',
+ groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
+ };
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ server.close(done);
+ });
+
+ it('Should fail if user is not in given group', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ groupCn: 'groupTheUserIsNotIn',
+ groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAP: User not in group');
+ }
+ server.close(done);
+ });
+
+ it('Should fail if the LDAP server does not allow searching inside the provided suffix', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example');
+ const options = {
+ suffix: 'o=invalid',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ groupCn: 'powerusers',
+ groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAP group search failed');
+ }
+ server.close(done);
+ });
+
+ it('Should fail if the LDAP server encounters an error while searching', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ groupCn: 'powerusers',
+ groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
+ };
+ try {
+ await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
+ fail();
+ } catch (err) {
+ expect(err.message).toBe('LDAP group search failed');
+ }
+ server.close(done);
+ });
+
+ it('Should delete the password from authData after validation', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ const authData = { id: 'testuser', password: 'secret' };
+ await ldap.validateAuthData(authData, options);
+ expect(authData).toEqual({ id: 'testuser' });
+ server.close(done);
+ });
+
+ it('Should not save the password in the user record after authentication', async done => {
+ const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
+ const options = {
+ suffix: 'o=example',
+ url: `ldap://localhost:${port}`,
+ dn: 'uid={{id}}, o=example',
+ };
+ await reconfigureServer({ auth: { ldap: options } });
+ const authData = { authData: { id: 'testuser', password: 'secret' } };
+ const returnedUser = await Parse.User.logInWith('ldap', authData);
+ const query = new Parse.Query('User');
+ const user = await query.equalTo('objectId', returnedUser.id).first({ useMasterKey: true });
+ expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
+ expect(user.get('authData').ldap.password).toBeUndefined();
+ server.close(done);
+ });
+});
diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js
new file mode 100644
index 0000000000..865c5b0c5c
--- /dev/null
+++ b/spec/Logger.spec.js
@@ -0,0 +1,97 @@
+const logging = require('../lib/Adapters/Logger/WinstonLogger');
+const Transport = require('winston-transport');
+
+class TestTransport extends Transport {
+ log(info, callback) {
+ callback(null, true);
+ }
+}
+
+describe('WinstonLogger', () => {
+ it('should add transport', () => {
+ const testTransport = new TestTransport();
+ spyOn(testTransport, 'log');
+ logging.addTransport(testTransport);
+ expect(logging.logger.transports.length).toBe(4);
+ logging.logger.info('hi');
+ expect(testTransport.log).toHaveBeenCalled();
+ logging.logger.error('error');
+ expect(testTransport.log).toHaveBeenCalled();
+ logging.removeTransport(testTransport);
+ expect(logging.logger.transports.length).toBe(3);
+ });
+
+ it('should have files transports', done => {
+ reconfigureServer().then(() => {
+ const transports = logging.logger.transports;
+ expect(transports.length).toBe(3);
+ done();
+ });
+ });
+
+ it('should disable files logs', done => {
+ reconfigureServer({
+ logsFolder: null,
+ })
+ .then(() => {
+ const transports = logging.logger.transports;
+ expect(transports.length).toBe(1);
+ return reconfigureServer();
+ })
+ .then(done);
+ });
+
+ it('should have a timestamp', done => {
+ logging.logger.info('hi');
+ logging.logger.query({ limit: 1 }, (err, results) => {
+ if (err) {
+ done.fail(err);
+ }
+ expect(results['parse-server'][0].timestamp).toBeDefined();
+ done();
+ });
+ });
+
+ it('console should not be json', done => {
+ // Force console transport
+ reconfigureServer({
+ logsFolder: null,
+ silent: false,
+ })
+ .then(() => {
+ spyOn(process.stdout, 'write');
+ logging.logger.info('hi', { key: 'value' });
+ expect(process.stdout.write).toHaveBeenCalled();
+ const firstLog = process.stdout.write.calls.first().args[0];
+ expect(firstLog).toEqual('info: hi {"key":"value"}' + '\n');
+ return reconfigureServer();
+ })
+ .then(() => {
+ done();
+ });
+ });
+
+ it('should enable JSON logs', done => {
+ // Force console transport
+ reconfigureServer({
+ logsFolder: null,
+ jsonLogs: true,
+ silent: false,
+ })
+ .then(() => {
+ spyOn(process.stdout, 'write');
+ logging.logger.info('hi', { key: 'value' });
+ expect(process.stdout.write).toHaveBeenCalled();
+ const firstLog = process.stdout.write.calls.first().args[0];
+ expect(firstLog).toEqual(
+ JSON.stringify({ key: 'value', level: 'info', message: 'hi' }) + '\n'
+ );
+ return reconfigureServer({
+ jsonLogs: false,
+ });
+ })
+ .then(() => {
+ done();
+ });
+ });
+});
diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js
index 3e992911c8..37d477444c 100644
--- a/spec/LoggerController.spec.js
+++ b/spec/LoggerController.spec.js
@@ -1,36 +1,39 @@
-var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
-var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
+const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
+const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
+ .WinstonLoggerAdapter;
describe('LoggerController', () => {
- it('can check process a query without throwing', (done) => {
+ it('can process an empty query without throwing', done => {
// Make mock request
- var query = {};
+ const query = {};
- var loggerController = new LoggerController(new FileLoggerAdapter());
+ const loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => {
- loggerController.getLogs(query).then(function(res) {
- expect(res.length).not.toBe(0);
- done();
- }).catch((err) =>Â {
- console.error(err);
- fail("should not fail");
- done();
- })
+ loggerController
+ .getLogs(query)
+ .then(function (res) {
+ expect(res.length).not.toBe(0);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ done();
+ });
}).not.toThrow();
});
- it('properly validates dateTimes', (done) => {
+ it('properly validates dateTimes', done => {
expect(LoggerController.validDateTime()).toBe(null);
- expect(LoggerController.validDateTime("String")).toBe(null);
+ expect(LoggerController.validDateTime('String')).toBe(null);
expect(LoggerController.validDateTime(123456).getTime()).toBe(123456);
- expect(LoggerController.validDateTime("2016-01-01Z00:00:00").getTime()).toBe(1451606400000);
+ expect(LoggerController.validDateTime('2016-01-01Z00:00:00').getTime()).toBe(1451606400000);
done();
});
- it('can set the proper default values', (done) => {
+ it('can set the proper default values', done => {
// Make mock request
- var result = LoggerController.parseOptions();
+ const result = LoggerController.parseOptions();
expect(result.size).toEqual(10);
expect(result.order).toEqual('desc');
expect(result.level).toEqual('info');
@@ -38,17 +41,17 @@ describe('LoggerController', () => {
done();
});
- it('can process a query without throwing', (done) => {
+ it('can parse an ascending query without throwing', done => {
// Make mock request
- var query = {
- from: "2016-01-01Z00:00:00",
- until: "2016-01-01Z00:00:00",
+ const query = {
+ from: '2016-01-01Z00:00:00',
+ until: '2016-01-01Z00:00:00',
size: 5,
order: 'asc',
- level: 'error'
+ level: 'error',
};
- var result = LoggerController.parseOptions(query);
+ const result = LoggerController.parseOptions(query);
expect(result.from.getTime()).toEqual(1451606400000);
expect(result.until.getTime()).toEqual(1451606400000);
@@ -59,34 +62,105 @@ describe('LoggerController', () => {
done();
});
- it('can check process a query without throwing', (done) => {
+ it('can process an ascending query without throwing', done => {
+ const query = {
+ size: 5,
+ order: 'asc',
+ level: 'error',
+ };
+
+ const loggerController = new LoggerController(new WinstonLoggerAdapter());
+ loggerController.error('can process an ascending query without throwing');
+
+ expect(() => {
+ loggerController
+ .getLogs(query)
+ .then(function (res) {
+ expect(res.length).not.toBe(0);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ fail('should not fail');
+ done();
+ });
+ }).not.toThrow();
+ });
+
+ it('can parse a descending query without throwing', done => {
// Make mock request
- var query = {
- from: "2016-01-01",
- until: "2016-01-30",
+ const query = {
+ from: '2016-01-01Z00:00:00',
+ until: '2016-01-01Z00:00:00',
size: 5,
order: 'desc',
- level: 'error'
+ level: 'error',
};
- var loggerController = new LoggerController(new FileLoggerAdapter());
+ const result = LoggerController.parseOptions(query);
+
+ expect(result.from.getTime()).toEqual(1451606400000);
+ expect(result.until.getTime()).toEqual(1451606400000);
+ expect(result.size).toEqual(5);
+ expect(result.order).toEqual('desc');
+ expect(result.level).toEqual('error');
+
+ done();
+ });
+
+ it('can process a descending query without throwing', done => {
+ const query = {
+ size: 5,
+ order: 'desc',
+ level: 'error',
+ };
+
+ const loggerController = new LoggerController(new WinstonLoggerAdapter());
+ loggerController.error('can process a descending query without throwing');
expect(() => {
- loggerController.getLogs(query).then(function(res) {
- expect(res.length).toBe(0);
- done();
- }).catch((err) =>Â {
- console.error(err);
- fail("should not fail");
- done();
- })
+ loggerController
+ .getLogs(query)
+ .then(function (res) {
+ expect(res.length).not.toBe(0);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ fail('should not fail');
+ done();
+ });
}).not.toThrow();
});
- it('should throw without an adapter', (done) => {
+ it('should throw without an adapter', done => {
expect(() => {
- var loggerController = new LoggerController();
+ new LoggerController();
}).toThrow();
done();
});
+
+ it('should replace implementations with verbose', done => {
+ const adapter = new WinstonLoggerAdapter();
+ const logger = new LoggerController(adapter, null, { verbose: true });
+ spyOn(adapter, 'log');
+ logger.silly('yo!');
+ expect(adapter.log).not.toHaveBeenCalled();
+ done();
+ });
+
+ it('should replace implementations with logLevel', done => {
+ const adapter = new WinstonLoggerAdapter();
+ const logger = new LoggerController(adapter, null, { logLevel: 'error' });
+ spyOn(adapter, 'log');
+ logger.warn('yo!');
+ logger.info('yo!');
+ logger.debug('yo!');
+ logger.verbose('yo!');
+ logger.silly('yo!');
+ expect(adapter.log).not.toHaveBeenCalled();
+ logger.error('error');
+ expect(adapter.log).toHaveBeenCalled();
+ done();
+ });
});
diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js
index e8907a39b6..d4b77baaa8 100644
--- a/spec/LogsRouter.spec.js
+++ b/spec/LogsRouter.spec.js
@@ -1,26 +1,29 @@
'use strict';
-const request = require('request');
-var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter;
-var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
-var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
+const request = require('../lib/request');
+const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter;
+const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
+const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
+ .WinstonLoggerAdapter;
-const loggerController = new LoggerController(new FileLoggerAdapter());
+const loggerController = new LoggerController(new WinstonLoggerAdapter());
-describe('LogsRouter', () => {
- it('can check valid master key of request', (done) => {
+describe_only(() => {
+ return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug';
+})('LogsRouter', () => {
+ it('can check valid master key of request', done => {
// Make mock request
- var request = {
+ const request = {
auth: {
- isMaster: true
+ isMaster: true,
},
query: {},
config: {
- loggerController: loggerController
- }
+ loggerController: loggerController,
+ },
};
- var router = new LogsRouter();
+ const router = new LogsRouter();
expect(() => {
router.validateRequest(request);
@@ -28,19 +31,19 @@ describe('LogsRouter', () => {
done();
});
- it('can check invalid construction of controller', (done) => {
+ it('can check invalid construction of controller', done => {
// Make mock request
- var request = {
+ const request = {
auth: {
- isMaster: true
+ isMaster: true,
},
query: {},
config: {
- loggerController: undefined // missing controller
- }
+ loggerController: undefined, // missing controller
+ },
};
- var router = new LogsRouter();
+ const router = new LogsRouter();
expect(() => {
router.validateRequest(request);
@@ -49,17 +52,126 @@ describe('LogsRouter', () => {
});
it('can check invalid master key of request', done => {
- request.get({
+ const logger = require('../lib/logger').default;
+ const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
+ loggerErrorSpy.calls.reset();
+ request({
url: 'http://localhost:8378/1/scriptlog',
- json: true,
headers: {
'X-Parse-Application-Id': 'test',
- 'X-Parse-REST-API-Key': 'rest'
- }
- }, (error, response, body) => {
- expect(response.statusCode).toEqual(403);
- expect(body.error).toEqual('unauthorized: master key is required');
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ }).then(fail, response => {
+ const body = response.data;
+ expect(response.status).toEqual(403);
+ expect(body.error).toEqual('Permission denied');
+ expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
});
});
+
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Master-Key': 'test',
+ };
+
+ /**
+ * Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log
+ */
+ it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)('does scrub simple passwords on GET login', done => {
+ reconfigureServer({
+ verbose: true,
+ }).then(function () {
+ request({
+ headers: headers,
+ url: 'http://localhost:8378/1/login?username=test&password=simplepass.com',
+ })
+ .catch(() => {})
+ .then(() => {
+ request({
+ url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
+ headers: headers,
+ }).then(response => {
+ const body = response.data;
+ expect(response.status).toEqual(200);
+ // 4th entry is our actual GET request
+ expect(body[2].url).toEqual('/1/login?username=test&password=********');
+ expect(body[2].message).toEqual(
+ 'REQUEST for [GET] /1/login?username=test&password=********: {}'
+ );
+ done();
+ });
+ });
+ });
+ });
+
+ /**
+ * Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log
+ */
+ it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)('does scrub complex passwords on GET login', done => {
+ reconfigureServer({
+ verbose: true,
+ })
+ .then(function () {
+ return request({
+ headers: headers,
+ // using urlencoded password, 'simple @,/?:&=+$#pass.com'
+ url:
+ 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com',
+ })
+ .catch(() => {})
+ .then(() => {
+ return request({
+ url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
+ headers: headers,
+ }).then(response => {
+ const body = response.data;
+ expect(response.status).toEqual(200);
+ // 4th entry is our actual GET request
+ expect(body[2].url).toEqual('/1/login?username=test&password=********');
+ expect(body[2].message).toEqual(
+ 'REQUEST for [GET] /1/login?username=test&password=********: {}'
+ );
+ done();
+ });
+ });
+ })
+ .catch(done.fail);
+ });
+
+ /**
+ * Verifies fields in POST login requests are NOT present in the verbose log
+ */
+ it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)('does not have password field in POST login', done => {
+ reconfigureServer({
+ verbose: true,
+ }).then(function () {
+ request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/login',
+ body: {
+ username: 'test',
+ password: 'simplepass.com',
+ },
+ })
+ .catch(() => {})
+ .then(() => {
+ request({
+ url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
+ headers: headers,
+ }).then(response => {
+ const body = response.data;
+ expect(response.status).toEqual(200);
+ // 4th entry is our actual GET request
+ expect(body[2].url).toEqual('/1/login');
+ expect(body[2].message).toEqual(
+ 'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}'
+ );
+ done();
+ });
+ });
+ });
+ });
});
diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js
index 45efc2fd2d..d05a56970b 100644
--- a/spec/Middlewares.spec.js
+++ b/spec/Middlewares.spec.js
@@ -1,69 +1,593 @@
-var middlewares = require('../src/middlewares');
-var AppCache = require('../src/cache').AppCache;
+const middlewares = require('../lib/middlewares');
+const AppCache = require('../lib/cache').AppCache;
+const { BlockList } = require('net');
+
+const AppCachePut = (appId, config) =>
+ AppCache.put(appId, {
+ ...config,
+ maintenanceKeyIpsStore: new Map(),
+ masterKeyIpsStore: new Map(),
+ readOnlyMasterKeyIpsStore: new Map(),
+ });
describe('middlewares', () => {
+ let fakeReq, fakeRes;
+ beforeEach(() => {
+ fakeReq = {
+ ip: '127.0.0.1',
+ originalUrl: 'http://example.com/parse/',
+ url: 'http://example.com/',
+ body: {
+ _ApplicationId: 'FakeAppId',
+ },
+ headers: {},
+ get: key => {
+ return fakeReq.headers[key.toLowerCase()];
+ },
+ };
+ fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status']);
+ AppCachePut(fakeReq.body._ApplicationId, {});
+ });
- var fakeReq, fakeRes;
+ afterEach(() => {
+ AppCache.del(fakeReq.body._ApplicationId);
+ });
- beforeEach(() => {
- fakeReq = {
- originalUrl: 'http://example.com/parse/',
- url: 'http://example.com/',
- body: {
- _ApplicationId: 'FakeAppId'
- },
- headers: {},
- get: (key) => {
- return fakeReq.headers[key.toLowerCase()]
- }
- };
- AppCache.put(fakeReq.body._ApplicationId, {});
+ it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)('should use _ContentType if provided', done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['127.0.0.1'],
+ });
+ expect(fakeReq.headers['content-type']).toEqual(undefined);
+ const contentType = 'image/jpeg';
+ fakeReq.body._ContentType = contentType;
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeReq.headers['content-type']).toEqual(contentType);
+ expect(fakeReq.body._ContentType).toEqual(undefined);
+ done();
+ });
+ });
+
+ it('should give invalid response when keys are configured but no key supplied', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ restAPIKey: 'restAPIKey',
+ });
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should give invalid response when keys are configured but supplied key is incorrect', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ restAPIKey: 'restAPIKey',
+ });
+ fakeReq.headers['x-parse-rest-api-key'] = 'wrongKey';
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should give invalid response when keys are configured but different key is supplied', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ restAPIKey: 'restAPIKey',
+ });
+ fakeReq.headers['x-parse-client-key'] = 'clientKey';
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should succeed when any one of the configured keys supplied', done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ clientKey: 'clientKey',
+ masterKey: 'masterKey',
+ restAPIKey: 'restAPIKey',
+ });
+ fakeReq.headers['x-parse-rest-api-key'] = 'restAPIKey';
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeRes.status).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('should succeed when client key supplied but empty', done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ clientKey: '',
+ masterKey: 'masterKey',
+ restAPIKey: 'restAPIKey',
+ });
+ fakeReq.headers['x-parse-client-key'] = '';
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeRes.status).not.toHaveBeenCalled();
+ done();
});
+ });
- afterEach(() => {
- AppCache.del(fakeReq.body._ApplicationId);
+ it('should succeed when no keys are configured and none supplied', done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ });
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeRes.status).not.toHaveBeenCalled();
+ done();
});
+ });
- it('should use _ContentType if provided', (done) => {
- expect(fakeReq.headers['content-type']).toEqual(undefined);
- var contentType = 'image/jpeg';
- fakeReq.body._ContentType = contentType;
- middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
- expect(fakeReq.headers['content-type']).toEqual(contentType);
- expect(fakeReq.body._ContentType).toEqual(undefined);
- done()
+ const BodyParams = {
+ clientVersion: '_ClientVersion',
+ installationId: '_InstallationId',
+ sessionToken: '_SessionToken',
+ masterKey: '_MasterKey',
+ javascriptKey: '_JavaScriptKey',
+ };
+
+ const BodyKeys = Object.keys(BodyParams);
+
+ BodyKeys.forEach(infoKey => {
+ const bodyKey = BodyParams[infoKey];
+ const keyValue = 'Fake' + bodyKey;
+ // javascriptKey is the only one that gets defaulted,
+ const otherKeys = BodyKeys.filter(
+ otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
+ );
+ it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)(`it should pull ${bodyKey} into req.info`, done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['0.0.0.0/0'],
+ });
+ fakeReq.ip = '127.0.0.1';
+ fakeReq.body[bodyKey] = keyValue;
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeReq.body[bodyKey]).toEqual(undefined);
+ expect(fakeReq.info[infoKey]).toEqual(keyValue);
+
+ otherKeys.forEach(otherKey => {
+ expect(fakeReq.info[otherKey]).toEqual(undefined);
});
+
+ done();
+ });
+ });
+ });
+
+ it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ masterKeyIps: ['10.0.0.1'],
+ });
+ fakeReq.ip = '127.0.0.1';
+ fakeReq.headers['x-parse-master-key'] = 'masterKey';
+
+ const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
+
+ expect(error).toBeDefined();
+ expect(error.message).toEqual(`unauthorized`);
+ expect(logger.error).toHaveBeenCalledWith(
+ `Request using master key rejected as the request IP address '127.0.0.1' is not set in Parse Server option 'masterKeyIps'.`
+ );
+ });
+
+ it('should not succeed and log if the ip does not belong to maintenanceKeyIps list', async () => {
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ AppCachePut(fakeReq.body._ApplicationId, {
+ maintenanceKey: 'masterKey',
+ maintenanceKeyIps: ['10.0.0.0', '10.0.0.1'],
+ });
+ fakeReq.ip = '10.0.0.2';
+ fakeReq.headers['x-parse-maintenance-key'] = 'masterKey';
+
+ const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
+
+ expect(error).toBeDefined();
+ expect(error.message).toEqual(`unauthorized`);
+ expect(logger.error).toHaveBeenCalledWith(
+ `Request using maintenance key rejected as the request IP address '10.0.0.2' is not set in Parse Server option 'maintenanceKeyIps'.`
+ );
+ });
+
+ it_id('5b8b9280-53ec-445a-b868-6992931d2236')(it)('should reject maintenance key from non-allowed IP instead of downgrading to anonymous auth', async () => {
+ await reconfigureServer({
+ maintenanceKeyIps: ['10.0.0.1'],
+ });
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ AppCachePut(fakeReq.body._ApplicationId, {
+ maintenanceKey: 'maintenanceKey',
+ maintenanceKeyIps: ['10.0.0.1'],
+ masterKey: 'masterKey',
+ masterKeyIps: ['0.0.0.0/0', '::0'],
+ });
+ fakeReq.ip = '127.0.0.1';
+ fakeReq.headers['x-parse-maintenance-key'] = 'maintenanceKey';
+
+ const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
+
+ expect(error).toBeDefined();
+ expect(error.status).toBe(403);
+ expect(error.message).toEqual('unauthorized');
+ expect(logger.error).toHaveBeenCalledWith(
+ `Request using maintenance key rejected as the request IP address '127.0.0.1' is not set in Parse Server option 'maintenanceKeyIps'.`
+ );
+ });
+
+ it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)('should succeed if the ip does belong to masterKeyIps list', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ masterKeyIps: ['10.0.0.1'],
+ });
+ fakeReq.ip = '10.0.0.1';
+ fakeReq.headers['x-parse-master-key'] = 'masterKey';
+ await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+ expect(fakeReq.auth.isMaster).toBe(true);
+ });
+
+ it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ masterKeyIps: ['0.0.0.0/0'],
+ });
+ fakeReq.ip = '10.0.0.1';
+ fakeReq.headers['x-parse-master-key'] = 'masterKey';
+ await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+ expect(fakeReq.auth.isMaster).toBe(true);
+ });
+
+ it('should not succeed and log if the ip does not belong to readOnlyMasterKeyIps list', async () => {
+ const logger = require('../lib/logger').logger;
+ spyOn(logger, 'error').and.callFake(() => {});
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['0.0.0.0/0'],
+ readOnlyMasterKey: 'readOnlyMasterKey',
+ readOnlyMasterKeyIps: ['10.0.0.1'],
+ });
+ fakeReq.ip = '127.0.0.1';
+ fakeReq.headers['x-parse-application-id'] = fakeReq.body._ApplicationId;
+ fakeReq.headers['x-parse-master-key'] = 'readOnlyMasterKey';
+
+ const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e);
+
+ expect(error).toBeDefined();
+ expect(error.message).toEqual('unauthorized');
+ expect(logger.error).toHaveBeenCalledWith(
+ `Request using read-only master key rejected as the request IP address '127.0.0.1' is not set in Parse Server option 'readOnlyMasterKeyIps'.`
+ );
+ });
+
+ it('should succeed if the ip does belong to readOnlyMasterKeyIps list', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['0.0.0.0/0'],
+ readOnlyMasterKey: 'readOnlyMasterKey',
+ readOnlyMasterKeyIps: ['10.0.0.1'],
+ });
+ fakeReq.ip = '10.0.0.1';
+ fakeReq.headers['x-parse-application-id'] = fakeReq.body._ApplicationId;
+ fakeReq.headers['x-parse-master-key'] = 'readOnlyMasterKey';
+ await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+ expect(fakeReq.auth.isMaster).toBe(true);
+ expect(fakeReq.auth.isReadOnly).toBe(true);
+ });
+
+ it('should allow any ip to use readOnlyMasterKey if readOnlyMasterKeyIps is 0.0.0.0/0', async () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['0.0.0.0/0'],
+ readOnlyMasterKey: 'readOnlyMasterKey',
+ readOnlyMasterKeyIps: ['0.0.0.0/0'],
});
+ fakeReq.ip = '10.0.0.1';
+ fakeReq.headers['x-parse-application-id'] = fakeReq.body._ApplicationId;
+ fakeReq.headers['x-parse-master-key'] = 'readOnlyMasterKey';
+ await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+ expect(fakeReq.auth.isMaster).toBe(true);
+ expect(fakeReq.auth.isReadOnly).toBe(true);
+ });
- const BodyParams = {
- clientVersion: '_ClientVersion',
- installationId: '_InstallationId',
- sessionToken: '_SessionToken',
- masterKey: '_MasterKey',
- javascriptKey: '_JavaScriptKey'
+ it('can set trust proxy', async () => {
+ const server = await reconfigureServer({ trustProxy: 1 });
+ expect(server.app.parent.settings['trust proxy']).toBe(1);
+ });
+
+ it('should properly expose the headers', () => {
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
};
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(Object.keys(headers).length).toBe(4);
+ expect(headers['Access-Control-Expose-Headers']).toBe(
+ 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id'
+ );
+ });
- const BodyKeys = Object.keys(BodyParams);
+ it('should set default Access-Control-Allow-Headers if allowHeaders are empty', () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowHeaders: undefined,
+ });
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
+ };
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS);
- BodyKeys.forEach((infoKey) => {
- const bodyKey = BodyParams[infoKey];
- const keyValue = 'Fake' + bodyKey;
- // javascriptKey is the only one that gets defaulted,
- const otherKeys = BodyKeys.filter((otherKey) => otherKey !== infoKey && otherKey !== 'javascriptKey');
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowHeaders: [],
+ });
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS);
+ });
- it(`it should pull ${bodyKey} into req.info`, (done) => {
- fakeReq.body[bodyKey] = keyValue;
+ it('should append custom headers to Access-Control-Allow-Headers if allowHeaders provided', () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowHeaders: ['Header-1', 'Header-2'],
+ });
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
+ };
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Headers']).toContain('Header-1, Header-2');
+ expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS);
+ });
- middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
- expect(fakeReq.body[bodyKey]).toEqual(undefined);
- expect(fakeReq.info[infoKey]).toEqual(keyValue);
+ it('should set default Access-Control-Allow-Origin if allowOrigin is empty', () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowOrigin: undefined,
+ });
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
+ };
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('*');
+ });
- otherKeys.forEach((otherKey) => {
- expect(fakeReq.info[otherKey]).toEqual(undefined);
- });
+ it('should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided', () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowOrigin: 'https://parseplatform.org/',
+ });
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
+ };
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/');
+ });
- done();
- });
- });
+ it('should support multiple origins if several are defined in allowOrigin as an array', () => {
+ AppCache.put(fakeReq.body._ApplicationId, {
+ allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'],
+ });
+ const headers = {};
+ const res = {
+ header: (key, value) => {
+ headers[key] = value;
+ },
+ };
+ const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
+ // Test with the first domain
+ fakeReq.headers.origin = 'https://a.com';
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
+ // Test with the second domain
+ fakeReq.headers.origin = 'https://b.com';
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com');
+ // Test with the third domain
+ fakeReq.headers.origin = 'https://c.com';
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com');
+ // Test with an unauthorized domain
+ fakeReq.headers.origin = 'https://unauthorized.com';
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
+ });
+
+ it('should use user provided on field userFromJWT', done => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ });
+ fakeReq.userFromJWT = 'fake-user';
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeReq.auth.user).toEqual('fake-user');
+ done();
+ });
+ });
+
+ it('should give invalid response when upload file without x-parse-application-id in header', () => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKey: 'masterKey',
+ });
+ fakeReq.body = Buffer.from('fake-file');
+ middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should match address', () => {
+ const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
+ const anotherIpv6 = '::ffff:101.10.0.1';
+ const ipv4 = '192.168.0.101';
+ const localhostV6 = '::1';
+ const localhostV62 = '::ffff:127.0.0.1';
+ const localhostV4 = '127.0.0.1';
+
+ const v6 = [ipv6, anotherIpv6];
+ v6.forEach(ip => {
+ expect(middlewares.checkIp(ip, ['::/0'], new Map())).toBe(true);
+ expect(middlewares.checkIp(ip, ['::'], new Map())).toBe(true);
+ expect(middlewares.checkIp(ip, ['0.0.0.0'], new Map())).toBe(false);
+ expect(middlewares.checkIp(ip, ['0.0.0.0/0'], new Map())).toBe(false);
+ expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe(false);
+ });
+
+ expect(middlewares.checkIp(ipv6, [anotherIpv6], new Map())).toBe(false);
+ expect(middlewares.checkIp(ipv6, [ipv6], new Map())).toBe(true);
+ expect(middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map())).toBe(true);
+
+ expect(middlewares.checkIp(ipv4, ['::'], new Map())).toBe(false);
+ expect(middlewares.checkIp(ipv4, ['::/0'], new Map())).toBe(false);
+ expect(middlewares.checkIp(ipv4, ['0.0.0.0'], new Map())).toBe(true);
+ expect(middlewares.checkIp(ipv4, ['0.0.0.0/0'], new Map())).toBe(true);
+ expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe(false);
+ expect(middlewares.checkIp(ipv4, [ipv4], new Map())).toBe(true);
+ expect(middlewares.checkIp(ipv4, ['192.168.0.0/24'], new Map())).toBe(true);
+
+ expect(middlewares.checkIp(localhostV4, ['::1'], new Map())).toBe(false);
+ expect(middlewares.checkIp(localhostV6, ['::1'], new Map())).toBe(true);
+ // ::ffff:127.0.0.1 is a padded ipv4 address but not ::1
+ expect(middlewares.checkIp(localhostV62, ['::1'], new Map())).toBe(false);
+ // ::ffff:127.0.0.1 is a padded ipv4 address and is a match for 127.0.0.1
+ expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe(true);
+ });
+
+ describe('body field type validation', () => {
+ beforeEach(() => {
+ AppCachePut(fakeReq.body._ApplicationId, {
+ masterKeyIps: ['0.0.0.0/0'],
+ });
+ });
+
+ it('should reject non-string _SessionToken in body', async () => {
+ fakeReq.body._SessionToken = { toString: 'evil' };
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should reject non-string _ClientVersion in body', async () => {
+ fakeReq.body._ClientVersion = { toLowerCase: 'evil' };
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should reject non-string _InstallationId in body', async () => {
+ fakeReq.body._InstallationId = { toString: 'evil' };
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
});
-});
\ No newline at end of file
+
+ it('should reject non-string _ContentType in body', async () => {
+ fakeReq.body._ContentType = { toString: 'evil' };
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should reject non-string base64 in file-via-JSON upload', async () => {
+ fakeReq.body = Buffer.from(
+ JSON.stringify({
+ _ApplicationId: 'FakeAppId',
+ base64: { toString: 'evil' },
+ })
+ );
+ await middlewares.handleParseHeaders(fakeReq, fakeRes);
+ expect(fakeRes.status).toHaveBeenCalledWith(403);
+ });
+
+ it('should not crash the server process on non-string body fields', async () => {
+ // Verify that type confusion in body fields does not crash the Node.js process.
+ // Each request should be handled independently without affecting server stability.
+ const payloads = [
+ { _SessionToken: { toString: 'evil' } },
+ { _ClientVersion: { toLowerCase: 'evil' } },
+ { _InstallationId: [1, 2, 3] },
+ { _ContentType: { toString: 'evil' } },
+ ];
+ for (const payload of payloads) {
+ const req = {
+ ip: '127.0.0.1',
+ originalUrl: 'http://example.com/parse/',
+ url: 'http://example.com/',
+ body: { _ApplicationId: 'FakeAppId', ...payload },
+ headers: {},
+ get: key => req.headers[key.toLowerCase()],
+ };
+ const res = jasmine.createSpyObj('res', ['end', 'status']);
+ await middlewares.handleParseHeaders(req, res);
+ expect(res.status).toHaveBeenCalledWith(403);
+ }
+ // Server process is still alive â a subsequent valid request works
+ const validReq = {
+ ip: '127.0.0.1',
+ originalUrl: 'http://example.com/parse/',
+ url: 'http://example.com/',
+ body: { _ApplicationId: 'FakeAppId' },
+ headers: {},
+ get: key => validReq.headers[key.toLowerCase()],
+ };
+ const validRes = jasmine.createSpyObj('validRes', ['end', 'status']);
+ let nextCalled = false;
+ await middlewares.handleParseHeaders(validReq, validRes, () => {
+ nextCalled = true;
+ });
+ expect(nextCalled).toBe(true);
+ expect(validRes.status).not.toHaveBeenCalled();
+ });
+
+ it('should still accept valid string body fields', done => {
+ fakeReq.body._SessionToken = 'r:validtoken';
+ fakeReq.body._ClientVersion = 'js1.0.0';
+ fakeReq.body._InstallationId = 'install123';
+ fakeReq.body._ContentType = 'application/json';
+ middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
+ expect(fakeReq.info.sessionToken).toEqual('r:validtoken');
+ expect(fakeReq.info.clientVersion).toEqual('js1.0.0');
+ expect(fakeReq.info.installationId).toEqual('install123');
+ expect(fakeReq.headers['content-type']).toEqual('application/json');
+ done();
+ });
+ });
+ });
+
+ it('should match address with cache', () => {
+ const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
+ const cache1 = new Map();
+ const spyBlockListCheck = spyOn(BlockList.prototype, 'check').and.callThrough();
+ expect(middlewares.checkIp(ipv6, ['::'], cache1)).toBe(true);
+ expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined);
+ expect(cache1.get('allowAllIpv6')).toBe(true);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(0);
+
+ const cache2 = new Map();
+ expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true);
+ expect(cache2.get('::1')).toBe(true);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ spyBlockListCheck.calls.reset();
+
+ const cache3 = new Map();
+ expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true);
+ expect(cache3.get('127.0.0.1')).toBe(true);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ spyBlockListCheck.calls.reset();
+
+ const cache4 = new Map();
+ const ranges = ['127.0.0.1', '192.168.0.0/24'];
+ // should not cache negative match
+ expect(middlewares.checkIp('123.123.123.123', ranges, cache4)).toBe(false);
+ expect(cache4.get('123.123.123.123')).toBe(undefined);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ spyBlockListCheck.calls.reset();
+
+ // should not cache cidr
+ expect(middlewares.checkIp('192.168.0.101', ranges, cache4)).toBe(true);
+ expect(cache4.get('192.168.0.101')).toBe(undefined);
+ expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/spec/MockAdapter.js b/spec/MockAdapter.js
deleted file mode 100644
index c3f557849d..0000000000
--- a/spec/MockAdapter.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = function(options) {
- return {
- options: options
- };
-};
diff --git a/spec/MockEmailAdapterWithOptions.js b/spec/MockEmailAdapterWithOptions.js
deleted file mode 100644
index 8a3095e21f..0000000000
--- a/spec/MockEmailAdapterWithOptions.js
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = options => {
- if (!options) {
- throw "Options were not provided"
- }
- return {
- sendVerificationEmail: () => Promise.resolve(),
- sendPasswordResetEmail: () => Promise.resolve(),
- sendMail: () => Promise.resolve()
- }
-}
diff --git a/spec/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js
index ba22c1fab3..8e376b9d1d 100644
--- a/spec/MongoSchemaCollectionAdapter.spec.js
+++ b/spec/MongoSchemaCollectionAdapter.spec.js
@@ -1,43 +1,58 @@
'use strict';
-const MongoSchemaCollection = require('../src/Adapters/Storage/Mongo/MongoSchemaCollection').default;
+const MongoSchemaCollection = require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection')
+ .default;
describe('MongoSchemaCollection', () => {
it('can transform legacy _client_permissions keys to parse format', done => {
- expect(MongoSchemaCollection._TESTmongoSchemaToParseSchema({
- "_id":"_Installation",
- "_client_permissions":{
- "get":true,
- "find":true,
- "update":true,
- "create":true,
- "delete":true,
- },
- "_metadata":{
- "class_permissions":{
- "get":{"*":true},
- "find":{"*":true},
- "update":{"*":true},
- "create":{"*":true},
- "delete":{"*":true},
- "addField":{"*":true},
- }
- },
- "installationId":"string",
- "deviceToken":"string",
- "deviceType":"string",
- "channels":"array",
- "user":"*_User",
- "pushType":"string",
- "GCMSenderId":"string",
- "timeZone":"string",
- "localeIdentifier":"string",
- "badge":"number",
- "appVersion":"string",
- "appName":"string",
- "appIdentifier":"string",
- "parseVersion":"string",
- })).toEqual({
+ expect(
+ MongoSchemaCollection._TESTmongoSchemaToParseSchema({
+ _id: '_Installation',
+ _client_permissions: {
+ get: true,
+ find: true,
+ count: true,
+ update: true,
+ create: true,
+ delete: true,
+ },
+ _metadata: {
+ class_permissions: {
+ ACL: {
+ '*': {
+ read: true,
+ write: true,
+ },
+ },
+ get: { '*': true },
+ find: { '*': true },
+ count: { '*': true },
+ update: { '*': true },
+ create: { '*': true },
+ delete: { '*': true },
+ addField: { '*': true },
+ protectedFields: { '*': [] },
+ },
+ indexes: {
+ name1: { deviceToken: 1 },
+ },
+ },
+ installationId: 'string',
+ deviceToken: 'string',
+ deviceType: 'string',
+ channels: 'array',
+ user: '*_User',
+ pushType: 'string',
+ GCMSenderId: 'string',
+ timeZone: 'string',
+ localeIdentifier: 'string',
+ badge: 'number',
+ appVersion: 'string',
+ appName: 'string',
+ appIdentifier: 'string',
+ parseVersion: 'string',
+ })
+ ).toEqual({
className: '_Installation',
fields: {
installationId: { type: 'String' },
@@ -60,13 +75,24 @@ describe('MongoSchemaCollection', () => {
objectId: { type: 'String' },
},
classLevelPermissions: {
+ ACL: {
+ '*': {
+ read: true,
+ write: true,
+ },
+ },
find: { '*': true },
get: { '*': true },
+ count: { '*': true },
create: { '*': true },
update: { '*': true },
delete: { '*': true },
addField: { '*': true },
- }
+ protectedFields: { '*': [] },
+ },
+ indexes: {
+ name1: { deviceToken: 1 },
+ },
});
done();
});
diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js
index 6c2666a4b0..facf1b2c41 100644
--- a/spec/MongoStorageAdapter.spec.js
+++ b/spec/MongoStorageAdapter.spec.js
@@ -1,22 +1,31 @@
'use strict';
-const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
-const MongoClient = require('mongodb').MongoClient;
+const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
+const { MongoClient, Collection } = require('mongodb');
const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
+const request = require('../lib/request');
+const Config = require('../lib/Config');
+const TestUtils = require('../lib/TestUtils');
+const Utils = require('../lib/Utils');
+const { randomUUID: uuidv4 } = require('crypto');
+
+const fakeClient = {
+ s: { options: { dbName: null } },
+ db: () => null,
+};
// These tests are specific to the mongo storage adapter + mongo storage format
// and will eventually be moved into their own repo
-describe('MongoStorageAdapter', () => {
- beforeEach(done => {
- new MongoStorageAdapter({ uri: databaseURI })
- .deleteAllClasses()
- .then(done, fail);
+describe_only_db('mongo')('MongoStorageAdapter', () => {
+ beforeEach(async () => {
+ await new MongoStorageAdapter({ uri: databaseURI }).deleteAllClasses();
+ Config.get(Parse.applicationId).schemaCache.clear();
});
it('auto-escapes symbols in auth information', () => {
- spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
+ spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
- uri: 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse'
+ uri: 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
@@ -25,9 +34,9 @@ describe('MongoStorageAdapter', () => {
});
it("doesn't double escape already URI-encoded information", () => {
- spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
+ spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
- uri: 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse'
+ uri: 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
@@ -35,11 +44,12 @@ describe('MongoStorageAdapter', () => {
);
});
- // https://github.com/ParsePlatform/parse-server/pull/148#issuecomment-180407057
+ // https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057
it('preserves replica sets', () => {
- spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
+ spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
- uri: 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415'
+ uri:
+ 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',
@@ -48,108 +58,1234 @@ describe('MongoStorageAdapter', () => {
});
it('stores objectId in _id', done => {
- let adapter = new MongoStorageAdapter({ uri: databaseURI });
- adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
- .then(() => adapter._rawFind('Foo', {}))
- .then(results => {
- expect(results.length).toEqual(1);
- var obj = results[0];
- expect(obj._id).toEqual('abcde');
- expect(obj.objectId).toBeUndefined();
- done();
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ adapter
+ .createObject('Foo', { fields: {} }, { objectId: 'abcde' })
+ .then(() => adapter._rawFind('Foo', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const obj = results[0];
+ expect(obj._id).toEqual('abcde');
+ expect(obj.objectId).toBeUndefined();
+ done();
+ });
+ });
+
+ it('find succeeds when query is within maxTimeMS', done => {
+ const maxTimeMS = 250;
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { maxTimeMS },
+ });
+ adapter
+ .createObject('Foo', { fields: {} }, { objectId: 'abcde' })
+ .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` }))
+ .then(
+ () => done(),
+ err => {
+ done.fail(`maxTimeMS should not affect fast queries ${err}`);
+ }
+ );
+ });
+
+ it('find fails when query exceeds maxTimeMS', done => {
+ const maxTimeMS = 250;
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { maxTimeMS },
+ });
+ adapter
+ .createObject('Foo', { fields: {} }, { objectId: 'abcde' })
+ .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` }))
+ .then(
+ () => {
+ done.fail('Find succeeded despite taking too long!');
+ },
+ err => {
+ expect(err.name).toEqual('MongoServerError');
+ expect(err.code).toEqual(50);
+ expect(err.message).toMatch('operation exceeded time limit');
+ done();
+ }
+ );
+ });
+
+ it('passes batchSize to the MongoDB driver find() call', async () => {
+ const batchSize = 50;
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { batchSize },
+ });
+ await adapter.createObject('BatchTest', { fields: {} }, { objectId: 'obj1' });
+
+ // Spy on the MongoDB driver's Collection.prototype.find to verify batchSize is forwarded
+ const originalFind = Collection.prototype.find;
+ let capturedOptions;
+ spyOn(Collection.prototype, 'find').and.callFake(function (query, options) {
+ capturedOptions = options;
+ return originalFind.call(this, query, options);
+ });
+
+ await adapter.find('BatchTest', { fields: {} }, {}, {});
+ expect(capturedOptions).toBeDefined();
+ expect(capturedOptions.batchSize).toEqual(50);
+ });
+
+ it('passes batchSize to the MongoDB driver aggregate() call', async () => {
+ const batchSize = 50;
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { batchSize },
+ });
+ await adapter.createObject('AggBatchTest', { fields: { count: { type: 'Number' } } }, { objectId: 'obj1', count: 1 });
+
+ // Spy on the MongoDB driver's Collection.prototype.aggregate to verify batchSize is forwarded
+ const originalAggregate = Collection.prototype.aggregate;
+ let capturedOptions;
+ spyOn(Collection.prototype, 'aggregate').and.callFake(function (pipeline, options) {
+ capturedOptions = options;
+ return originalAggregate.call(this, pipeline, options);
});
+
+ await adapter.aggregate('AggBatchTest', { fields: { count: { type: 'Number' } } }, [{ $match: {} }]);
+ expect(capturedOptions).toBeDefined();
+ expect(capturedOptions.batchSize).toEqual(50);
});
- it('stores pointers with a _p_ prefix', (done) => {
- let obj = {
+ it('defaults batchSize to 1000', async () => {
+ await reconfigureServer({
+ databaseURI: databaseURI,
+ collectionPrefix: 'test_',
+ databaseAdapter: undefined,
+ });
+ const adapter = Config.get(Parse.applicationId).database.adapter;
+ expect(adapter._batchSize).toEqual(1000);
+ });
+
+ it('stores pointers with a _p_ prefix', done => {
+ const obj = {
objectId: 'bar',
aPointer: {
__type: 'Pointer',
className: 'JustThePointer',
- objectId: 'qwerty'
- }
+ objectId: 'qwerty',
+ },
};
- let adapter = new MongoStorageAdapter({ uri: databaseURI });
- adapter.createObject('APointerDarkly', { fields: {
- objectId: { type: 'String' },
- aPointer: { type: 'Pointer', targetClass: 'JustThePointer' },
- }}, obj)
- .then(() => adapter._rawFind('APointerDarkly', {}))
- .then(results => {
- expect(results.length).toEqual(1);
- let output = results[0];
- expect(typeof output._id).toEqual('string');
- expect(typeof output._p_aPointer).toEqual('string');
- expect(output._p_aPointer).toEqual('JustThePointer$qwerty');
- expect(output.aPointer).toBeUndefined();
- done();
- });
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ adapter
+ .createObject(
+ 'APointerDarkly',
+ {
+ fields: {
+ objectId: { type: 'String' },
+ aPointer: { type: 'Pointer', targetClass: 'JustThePointer' },
+ },
+ },
+ obj
+ )
+ .then(() => adapter._rawFind('APointerDarkly', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const output = results[0];
+ expect(typeof output._id).toEqual('string');
+ expect(typeof output._p_aPointer).toEqual('string');
+ expect(output._p_aPointer).toEqual('JustThePointer$qwerty');
+ expect(output.aPointer).toBeUndefined();
+ done();
+ });
});
it('handles object and subdocument', done => {
- let adapter = new MongoStorageAdapter({ uri: databaseURI });
- let schema = { fields : { subdoc: { type: 'Object' } } };
- let obj = { subdoc: {foo: 'bar', wu: 'tan'} };
- adapter.createObject('MyClass', schema, obj)
- .then(() => adapter._rawFind('MyClass', {}))
- .then(results => {
- expect(results.length).toEqual(1);
- let mob = results[0];
- expect(typeof mob.subdoc).toBe('object');
- expect(mob.subdoc.foo).toBe('bar');
- expect(mob.subdoc.wu).toBe('tan');
- let obj = { 'subdoc.wu': 'clan' };
- return adapter.findOneAndUpdate('MyClass', schema, {}, obj);
- })
- .then(() => adapter._rawFind('MyClass', {}))
- .then(results => {
- expect(results.length).toEqual(1);
- let mob = results[0];
- expect(typeof mob.subdoc).toBe('object');
- expect(mob.subdoc.foo).toBe('bar');
- expect(mob.subdoc.wu).toBe('clan');
- done();
- });
- });
-
- it('handles array, object, date', (done) => {
- let adapter = new MongoStorageAdapter({ uri: databaseURI });
- let obj = {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ const schema = { fields: { subdoc: { type: 'Object' } } };
+ const obj = { subdoc: { foo: 'bar', wu: 'tan' } };
+ adapter
+ .createObject('MyClass', schema, obj)
+ .then(() => adapter._rawFind('MyClass', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const mob = results[0];
+ expect(typeof mob.subdoc).toBe('object');
+ expect(mob.subdoc.foo).toBe('bar');
+ expect(mob.subdoc.wu).toBe('tan');
+ const obj = { 'subdoc.wu': 'clan' };
+ return adapter.findOneAndUpdate('MyClass', schema, {}, obj);
+ })
+ .then(() => adapter._rawFind('MyClass', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const mob = results[0];
+ expect(typeof mob.subdoc).toBe('object');
+ expect(mob.subdoc.foo).toBe('bar');
+ expect(mob.subdoc.wu).toBe('clan');
+ done();
+ });
+ });
+
+ it('handles creating an array, object, date', done => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ const obj = {
array: [1, 2, 3],
- object: {foo: 'bar'},
+ object: { foo: 'bar' },
date: {
__type: 'Date',
iso: '2016-05-26T20:55:01.154Z',
},
};
- let schema = { fields: {
- array: { type: 'Array' },
- object: { type: 'Object' },
- date: { type: 'Date' },
- } };
- adapter.createObject('MyClass', schema, obj)
- .then(() => adapter._rawFind('MyClass', {}))
- .then(results => {
- expect(results.length).toEqual(1);
- let mob = results[0];
- expect(mob.array instanceof Array).toBe(true);
- expect(typeof mob.object).toBe('object');
- expect(mob.date instanceof Date).toBe(true);
- return adapter.find('MyClass', schema, {}, {});
- })
- .then(results => {
- expect(results.length).toEqual(1);
- let mob = results[0];
- expect(mob.array instanceof Array).toBe(true);
- expect(typeof mob.object).toBe('object');
- expect(mob.date.__type).toBe('Date');
- expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z');
- done();
- })
- .catch(error => {
- console.log(error);
- fail();
- done();
+ const schema = {
+ fields: {
+ array: { type: 'Array' },
+ object: { type: 'Object' },
+ date: { type: 'Date' },
+ },
+ };
+ adapter
+ .createObject('MyClass', schema, obj)
+ .then(() => adapter._rawFind('MyClass', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const mob = results[0];
+ expect(Array.isArray(mob.array)).toBe(true);
+ expect(typeof mob.object).toBe('object');
+ expect(Utils.isDate(mob.date)).toBe(true);
+ return adapter.find('MyClass', schema, {}, {});
+ })
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const mob = results[0];
+ expect(Array.isArray(mob.array)).toBe(true);
+ expect(typeof mob.object).toBe('object');
+ expect(mob.date.__type).toBe('Date');
+ expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z');
+ done();
+ })
+ .catch(error => {
+ console.log(error);
+ fail();
+ done();
+ });
+ });
+
+ it('handles nested dates', async () => {
+ await new Parse.Object('MyClass', {
+ foo: {
+ test: {
+ date: new Date(),
+ },
+ },
+ bar: {
+ date: new Date(),
+ },
+ date: new Date(),
+ }).save();
+ const adapter = Config.get(Parse.applicationId).database.adapter;
+ const [object] = await adapter._rawFind('MyClass', {});
+ expect(Utils.isDate(object.date)).toBeTrue();
+ expect(Utils.isDate(object.bar.date)).toBeTrue();
+ expect(Utils.isDate(object.foo.test.date)).toBeTrue();
+ });
+
+ it('handles nested dates in array ', async () => {
+ await new Parse.Object('MyClass', {
+ foo: {
+ test: {
+ date: [new Date()],
+ },
+ },
+ bar: {
+ date: [new Date()],
+ },
+ date: [new Date()],
+ }).save();
+ const adapter = Config.get(Parse.applicationId).database.adapter;
+ const [object] = await adapter._rawFind('MyClass', {});
+ expect(Utils.isDate(object.date[0])).toBeTrue();
+ expect(Utils.isDate(object.bar.date[0])).toBeTrue();
+ expect(Utils.isDate(object.foo.test.date[0])).toBeTrue();
+ const obj = await new Parse.Query('MyClass').first({ useMasterKey: true });
+ expect(Utils.isDate(obj.get('date')[0])).toBeTrue();
+ expect(Utils.isDate(obj.get('bar').date[0])).toBeTrue();
+ expect(Utils.isDate(obj.get('foo').test.date[0])).toBeTrue();
+ });
+
+ it('upserts with $setOnInsert', async () => {
+ const uuid1 = uuidv4();
+ const uuid2 = uuidv4();
+ const schema = {
+ className: 'MyClass',
+ fields: {
+ x: { type: 'Number' },
+ count: { type: 'Number' },
+ },
+ classLevelPermissions: {},
+ };
+
+ const myClassSchema = new Parse.Schema(schema.className);
+ myClassSchema.setCLP(schema.classLevelPermissions);
+ await myClassSchema.save();
+
+ const query = {
+ x: 1,
+ };
+ const update = {
+ objectId: {
+ __op: 'SetOnInsert',
+ amount: uuid1,
+ },
+ count: {
+ __op: 'Increment',
+ amount: 1,
+ },
+ };
+ await Parse.Server.database.update('MyClass', query, update, { upsert: true });
+ update.objectId.amount = uuid2;
+ await Parse.Server.database.update('MyClass', query, update, { upsert: true });
+
+ const res = await Parse.Server.database.find(schema.className, {}, {});
+ expect(res.length).toBe(1);
+ expect(res[0].objectId).toBe(uuid1);
+ expect(res[0].count).toBe(2);
+ expect(res[0].x).toBe(1);
+ });
+
+ it('handles updating a single object with array, object date', done => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+
+ const schema = {
+ fields: {
+ array: { type: 'Array' },
+ object: { type: 'Object' },
+ date: { type: 'Date' },
+ },
+ };
+
+ adapter
+ .createObject('MyClass', schema, {})
+ .then(() => adapter._rawFind('MyClass', {}))
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const update = {
+ array: [1, 2, 3],
+ object: { foo: 'bar' },
+ date: {
+ __type: 'Date',
+ iso: '2016-05-26T20:55:01.154Z',
+ },
+ };
+ const query = {};
+ return adapter.findOneAndUpdate('MyClass', schema, query, update);
+ })
+ .then(results => {
+ const mob = results;
+ expect(Array.isArray(mob.array)).toBe(true);
+ expect(typeof mob.object).toBe('object');
+ expect(mob.date.__type).toBe('Date');
+ expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z');
+ return adapter._rawFind('MyClass', {});
+ })
+ .then(results => {
+ expect(results.length).toEqual(1);
+ const mob = results[0];
+ expect(Array.isArray(mob.array)).toBe(true);
+ expect(typeof mob.object).toBe('object');
+ expect(Utils.isDate(mob.date)).toBe(true);
+ done();
+ })
+ .catch(error => {
+ console.log(error);
+ fail();
+ done();
+ });
+ });
+
+ it('handleShutdown, close connection', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+
+ const schema = {
+ fields: {
+ array: { type: 'Array' },
+ object: { type: 'Object' },
+ date: { type: 'Date' },
+ },
+ };
+
+ await adapter.createObject('MyClass', schema, {});
+ const status = await adapter.database.admin().serverStatus();
+ expect(status.connections.current > 0).toEqual(true);
+
+ await adapter.handleShutdown();
+ try {
+ await adapter.database.admin().serverStatus();
+ expect(false).toBe(true);
+ } catch (e) {
+ expect(e.message).toEqual('Client must be connected before running operations');
+ }
+ });
+
+ it('getClass if exists', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+
+ const schema = {
+ fields: {
+ array: { type: 'Array' },
+ object: { type: 'Object' },
+ date: { type: 'Date' },
+ },
+ };
+
+ await adapter.createClass('MyClass', schema);
+ const myClassSchema = await adapter.getClass('MyClass');
+ expect(myClassSchema).toBeDefined();
+ });
+
+ it('getClass if not exists', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined);
+ });
+
+ it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => {
+ const user = new Parse.User();
+ user.set('username', 'Bugs');
+ user.set('password', 'Bunny');
+ await user.signUp();
+
+ const database = Config.get(Parse.applicationId).database;
+ await database.adapter.dropAllIndexes('_User');
+
+ const preIndexPlan = await database.find(
+ '_User',
+ { username: 'bugs' },
+ { caseInsensitive: true, explain: true }
+ );
+
+ const schema = await new Parse.Schema('_User').get();
+
+ await database.adapter.ensureIndex(
+ '_User',
+ schema,
+ ['username'],
+ 'case_insensitive_username',
+ true
+ );
+
+ const postIndexPlan = await database.find(
+ '_User',
+ { username: 'bugs' },
+ { caseInsensitive: true, explain: true }
+ );
+ expect(preIndexPlan.executionStats.executionStages.stage).toBe('COLLSCAN');
+ expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH');
+ });
+
+ it('should delete field without index', async () => {
+ const database = Config.get(Parse.applicationId).database;
+ const obj = new Parse.Object('MyObject');
+ obj.set('test', 1);
+ await obj.save();
+ const schemaBeforeDeletion = await new Parse.Schema('MyObject').get();
+ await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']);
+ const schemaAfterDeletion = await new Parse.Schema('MyObject').get();
+ expect(schemaBeforeDeletion.fields.test).toBeDefined();
+ expect(schemaAfterDeletion.fields.test).toBeUndefined();
+ });
+
+ it('should delete field with index', async () => {
+ const database = Config.get(Parse.applicationId).database;
+ const obj = new Parse.Object('MyObject');
+ obj.set('test', 1);
+ await obj.save();
+ const schemaBeforeDeletion = await new Parse.Schema('MyObject').get();
+ await database.adapter.ensureIndex('MyObject', schemaBeforeDeletion, ['test']);
+ await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']);
+ const schemaAfterDeletion = await new Parse.Schema('MyObject').get();
+ expect(schemaBeforeDeletion.fields.test).toBeDefined();
+ expect(schemaAfterDeletion.fields.test).toBeUndefined();
+ });
+
+ it('should create index with partialFilterExpression', async () => {
+ const database = Config.get(Parse.applicationId).database;
+ const adapter = database.adapter;
+
+ const user = new Parse.User();
+ user.set('username', 'testuser');
+ user.set('password', 'testpass');
+ await user.signUp();
+
+ const schema = await new Parse.Schema('_User').get();
+ const partialFilterExpression = { _email_verify_token: { $exists: true } };
+
+ await adapter.ensureIndex('_User', schema, ['username'], 'partial_username_index', false, {
+ partialFilterExpression,
+ sparse: false,
+ });
+
+ const indexes = await adapter.getIndexes('_User');
+ const createdIndex = indexes.find(idx => idx.name === 'partial_username_index');
+ expect(createdIndex).toBeDefined();
+ expect(createdIndex.partialFilterExpression).toEqual({ _email_verify_token: { $exists: true } });
+ expect(createdIndex.sparse).toBeFalsy();
+ });
+
+ if (process.env.MONGODB_TOPOLOGY === 'replicaset') {
+ describe('transactions', () => {
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+
+ beforeEach(async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI:
+ 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
+ });
+ await TestUtils.destroyAllDataPermanently(true);
+ });
+
+ it('should use transaction in a batch with transaction = true', async () => {
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
+
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/batch',
+ body: JSON.stringify({
+ requests: [
+ {
+ method: 'PUT',
+ path: '/1/classes/MyObject/' + myObject.id,
+ body: { myAttribute: 'myValue' },
+ },
+ ],
+ transaction: true,
+ }),
+ });
+
+ let found = false;
+ Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
+ found = true;
+ expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED');
+ });
+ expect(found).toBe(true);
+ });
+
+ it('should not use transaction in a batch with transaction = false', async () => {
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
+
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/batch',
+ body: JSON.stringify({
+ requests: [
+ {
+ method: 'PUT',
+ path: '/1/classes/MyObject/' + myObject.id,
+ body: { myAttribute: 'myValue' },
+ },
+ ],
+ transaction: false,
+ }),
+ });
+
+ let found = false;
+ Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
+ found = true;
+ expect(call.args[2].session).toBeFalsy();
+ });
+ expect(found).toBe(true);
+ });
+
+ it('should not use transaction in a batch with no transaction option sent', async () => {
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
+
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/batch',
+ body: JSON.stringify({
+ requests: [
+ {
+ method: 'PUT',
+ path: '/1/classes/MyObject/' + myObject.id,
+ body: { myAttribute: 'myValue' },
+ },
+ ],
+ }),
+ });
+
+ let found = false;
+ Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
+ found = true;
+ expect(call.args[2].session).toBeFalsy();
+ });
+ expect(found).toBe(true);
+ });
+
+ it('should not use transaction in a put request', async () => {
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
+
+ await request({
+ method: 'PUT',
+ headers: headers,
+ url: 'http://localhost:8378/1/classes/MyObject/' + myObject.id,
+ body: { myAttribute: 'myValue' },
+ });
+
+ let found = false;
+ Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
+ found = true;
+ expect(call.args[2].session).toBeFalsy();
+ });
+ expect(found).toBe(true);
+ });
+
+ it('should not use transactions when using SDK insert', async () => {
+ spyOn(Collection.prototype, 'insertOne').and.callThrough();
+
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ const calls = Collection.prototype.insertOne.calls.all();
+ expect(calls.length).toBeGreaterThan(0);
+ calls.forEach(call => {
+ expect(call.args[1].session).toBeFalsy();
+ });
+ });
+
+ it('should not use transactions when using SDK update', async () => {
+ spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
+
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ myObject.set('myAttribute', 'myValue');
+ await myObject.save();
+
+ const calls = Collection.prototype.findOneAndUpdate.calls.all();
+ expect(calls.length).toBeGreaterThan(0);
+ calls.forEach(call => {
+ expect(call.args[2].session).toBeFalsy();
+ });
+ });
+
+ it('should not use transactions when using SDK delete', async () => {
+ spyOn(Collection.prototype, 'deleteMany').and.callThrough();
+
+ const myObject = new Parse.Object('MyObject');
+ await myObject.save();
+
+ await myObject.destroy();
+
+ const calls = Collection.prototype.deleteMany.calls.all();
+ expect(calls.length).toBeGreaterThan(0);
+ calls.forEach(call => {
+ expect(call.args[1].session).toBeFalsy();
+ });
+ });
+ });
+
+ describe('watch _SCHEMA', () => {
+ it('should change', async done => {
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ collectionPrefix: '',
+ mongoOptions: { enableSchemaHooks: true },
+ });
+ await reconfigureServer({ databaseAdapter: adapter });
+ expect(adapter.enableSchemaHooks).toBe(true);
+ spyOn(adapter, '_onchange');
+ const schema = {
+ fields: {
+ array: { type: 'Array' },
+ object: { type: 'Object' },
+ date: { type: 'Date' },
+ },
+ };
+
+ await adapter.createClass('Stuff', schema);
+ const myClassSchema = await adapter.getClass('Stuff');
+ expect(myClassSchema).toBeDefined();
+ setTimeout(() => {
+ expect(adapter._onchange).toHaveBeenCalled();
+ done();
+ }, 5000);
+ });
+ });
+ }
+
+ describe('index creation options', () => {
+ beforeEach(async () => {
+ await new MongoStorageAdapter({ uri: databaseURI }).deleteAllClasses();
+ });
+
+ async function getIndexes(collectionName) {
+ const adapter = Config.get(Parse.applicationId).database.adapter;
+ const collections = await adapter.database.listCollections({ name: collectionName }).toArray();
+ if (collections.length === 0) {
+ return [];
+ }
+ return await adapter.database.collection(collectionName).indexes();
+ }
+
+ it('should skip username index when createIndexUserUsername is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserUsername: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'username_1')).toBeUndefined();
+ });
+
+ it('should create username index when createIndexUserUsername is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserUsername: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'username_1')).toBeDefined();
+ });
+
+ it('should skip case-insensitive username index when createIndexUserUsernameCaseInsensitive is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserUsernameCaseInsensitive: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'case_insensitive_username')).toBeUndefined();
+ });
+
+ it('should create case-insensitive username index when createIndexUserUsernameCaseInsensitive is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserUsernameCaseInsensitive: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'case_insensitive_username')).toBeDefined();
+ });
+
+ it('should skip email index when createIndexUserEmail is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmail: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'email_1')).toBeUndefined();
+ });
+
+ it('should create email index when createIndexUserEmail is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmail: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'email_1')).toBeDefined();
+ });
+
+ it('should skip case-insensitive email index when createIndexUserEmailCaseInsensitive is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmailCaseInsensitive: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'case_insensitive_email')).toBeUndefined();
+ });
+
+ it('should create case-insensitive email index when createIndexUserEmailCaseInsensitive is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmailCaseInsensitive: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === 'case_insensitive_email')).toBeDefined();
+ });
+
+ it('should skip email verify token index when createIndexUserEmailVerifyToken is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmailVerifyToken: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === '_email_verify_token' || idx.name === '_email_verify_token_1')).toBeUndefined();
+ });
+
+ it('should create email verify token index when createIndexUserEmailVerifyToken is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserEmailVerifyToken: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === '_email_verify_token' || idx.name === '_email_verify_token_1')).toBeDefined();
+ });
+
+ it('should skip password reset token index when createIndexUserPasswordResetToken is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserPasswordResetToken: false },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === '_perishable_token' || idx.name === '_perishable_token_1')).toBeUndefined();
+ });
+
+ it('should create password reset token index when createIndexUserPasswordResetToken is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexUserPasswordResetToken: true },
+ });
+ const indexes = await getIndexes('_User');
+ expect(indexes.find(idx => idx.name === '_perishable_token' || idx.name === '_perishable_token_1')).toBeDefined();
+ });
+
+ it('should skip role name index when createIndexRoleName is false', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexRoleName: false },
+ });
+ const indexes = await getIndexes('_Role');
+ expect(indexes.find(idx => idx.name === 'name_1')).toBeUndefined();
+ });
+
+ it('should create role name index when createIndexRoleName is true', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: { createIndexRoleName: true },
+ });
+ const indexes = await getIndexes('_Role');
+ expect(indexes.find(idx => idx.name === 'name_1')).toBeDefined();
+ });
+
+ it('should create all indexes by default when options are undefined', async () => {
+ await reconfigureServer({
+ databaseAdapter: undefined,
+ databaseURI,
+ databaseOptions: {},
+ });
+
+ const userIndexes = await getIndexes('_User');
+ const roleIndexes = await getIndexes('_Role');
+
+ // Verify all indexes are created with default behavior (backward compatibility)
+ expect(userIndexes.find(idx => idx.name === 'username_1')).toBeDefined();
+ expect(userIndexes.find(idx => idx.name === 'case_insensitive_username')).toBeDefined();
+ expect(userIndexes.find(idx => idx.name === 'email_1')).toBeDefined();
+ expect(userIndexes.find(idx => idx.name === 'case_insensitive_email')).toBeDefined();
+ expect(userIndexes.find(idx => idx.name === '_email_verify_token' || idx.name === '_email_verify_token_1')).toBeDefined();
+ expect(userIndexes.find(idx => idx.name === '_perishable_token' || idx.name === '_perishable_token_1')).toBeDefined();
+ expect(roleIndexes.find(idx => idx.name === 'name_1')).toBeDefined();
+ });
+ });
+
+ describe('logClientEvents', () => {
+ it('should log MongoDB client events when configured', async () => {
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn');
+
+ const logClientEvents = [
+ {
+ name: 'serverDescriptionChanged',
+ keys: ['address'],
+ logLevel: 'warn',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ // Connect to trigger event listeners setup
+ await adapter.connect();
+
+ // Manually trigger the event to test the listener
+ const mockEvent = {
+ address: 'localhost:27017',
+ previousDescription: { type: 'Unknown' },
+ newDescription: { type: 'Standalone' },
+ };
+
+ adapter.client.emit('serverDescriptionChanged', mockEvent);
+
+ // Verify the log was called with the correct message
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event serverDescriptionChanged:.*"address":"localhost:27017"/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should log entire event when keys are not specified', async () => {
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'info');
+
+ const logClientEvents = [
+ {
+ name: 'connectionPoolReady',
+ logLevel: 'info',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ const mockEvent = {
+ address: 'localhost:27017',
+ options: { maxPoolSize: 100 },
+ };
+
+ adapter.client.emit('connectionPoolReady', mockEvent);
+
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event connectionPoolReady:.*"address":"localhost:27017".*"options"/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should extract nested keys using dot notation', async () => {
+ const logger = require('../lib/logger').logger;
+ const logSpy = spyOn(logger, 'warn');
+
+ const logClientEvents = [
+ {
+ name: 'topologyDescriptionChanged',
+ keys: ['previousDescription.type', 'newDescription.type', 'newDescription.servers.size'],
+ logLevel: 'warn',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ const mockEvent = {
+ topologyId: 1,
+ previousDescription: { type: 'Unknown' },
+ newDescription: {
+ type: 'ReplicaSetWithPrimary',
+ servers: { size: 3 },
+ },
+ };
+
+ adapter.client.emit('topologyDescriptionChanged', mockEvent);
+
+ expect(logSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event topologyDescriptionChanged:.*"previousDescription.type":"Unknown".*"newDescription.type":"ReplicaSetWithPrimary".*"newDescription.servers.size":3/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should handle invalid log level gracefully', async () => {
+ const logger = require('../lib/logger').logger;
+ const infoSpy = spyOn(logger, 'info');
+
+ const logClientEvents = [
+ {
+ name: 'connectionPoolReady',
+ keys: ['address'],
+ logLevel: 'invalidLogLevel', // Invalid log level
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ const mockEvent = {
+ address: 'localhost:27017',
+ };
+
+ adapter.client.emit('connectionPoolReady', mockEvent);
+
+ // Should fallback to 'info' level
+ expect(infoSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event connectionPoolReady:.*"address":"localhost:27017"/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should handle Map and Set instances in events', async () => {
+ const logger = require('../lib/logger').logger;
+ const warnSpy = spyOn(logger, 'warn');
+
+ const logClientEvents = [
+ {
+ name: 'customEvent',
+ logLevel: 'warn',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ const mockEvent = {
+ mapData: new Map([['key1', 'value1'], ['key2', 'value2']]),
+ setData: new Set([1, 2, 3]),
+ };
+
+ adapter.client.emit('customEvent', mockEvent);
+
+ // Should serialize Map and Set properly
+ expect(warnSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event customEvent:.*"mapData":\{"key1":"value1","key2":"value2"\}.*"setData":\[1,2,3\]/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should handle missing keys in event object', async () => {
+ const logger = require('../lib/logger').logger;
+ const infoSpy = spyOn(logger, 'info');
+
+ const logClientEvents = [
+ {
+ name: 'testEvent',
+ keys: ['nonexistent.nested.key', 'another.missing'],
+ logLevel: 'info',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ const mockEvent = {
+ actualField: 'value',
+ };
+
+ adapter.client.emit('testEvent', mockEvent);
+
+ // Should handle missing keys gracefully with undefined values
+ expect(infoSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event testEvent:/)
+ );
+
+ await adapter.handleShutdown();
+ });
+
+ it('should handle circular references gracefully', async () => {
+ const logger = require('../lib/logger').logger;
+ const infoSpy = spyOn(logger, 'info');
+
+ const logClientEvents = [
+ {
+ name: 'circularEvent',
+ logLevel: 'info',
+ },
+ ];
+
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { logClientEvents },
+ });
+
+ await adapter.connect();
+
+ // Create circular reference
+ const mockEvent = { name: 'test' };
+ mockEvent.self = mockEvent;
+
+ adapter.client.emit('circularEvent', mockEvent);
+
+ // Should handle circular reference with [Circular] marker
+ expect(infoSpy).toHaveBeenCalledWith(
+ jasmine.stringMatching(/MongoDB client event circularEvent:.*\[Circular\]/)
+ );
+
+ await adapter.handleShutdown();
+ });
+ });
+
+ describe('transient error handling', () => {
+ it('should transform MongoWaitQueueTimeoutError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ // Create a mock error with the MongoWaitQueueTimeoutError name
+ const mockError = new Error('Timed out while checking out a connection from connection pool');
+ mockError.name = 'MongoWaitQueueTimeoutError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(true);
+ expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
+ expect(error.message).toBe('Database error');
+ }
+ });
+
+ it('should transform MongoServerSelectionError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ const mockError = new Error('Server selection timed out');
+ mockError.name = 'MongoServerSelectionError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(true);
+ expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
+ expect(error.message).toBe('Database error');
+ }
+ });
+
+ it('should transform MongoNetworkTimeoutError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ const mockError = new Error('Network timeout');
+ mockError.name = 'MongoNetworkTimeoutError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(true);
+ expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
+ expect(error.message).toBe('Database error');
+ }
+ });
+
+ it('should transform MongoNetworkError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ const mockError = new Error('Network error');
+ mockError.name = 'MongoNetworkError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(true);
+ expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
+ expect(error.message).toBe('Database error');
+ }
+ });
+
+ it('should transform TransientTransactionError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ const mockError = new Error('Transient transaction error');
+ mockError.hasErrorLabel = label => label === 'TransientTransactionError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(true);
+ expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
+ expect(error.message).toBe('Database error');
+ }
+ });
+
+ it('should not transform non-transient errors', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ const mockError = new Error('Some other error');
+ mockError.name = 'SomeOtherError';
+
+ try {
+ adapter.handleError(mockError);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error instanceof Parse.Error).toBe(false);
+ expect(error.message).toBe('Some other error');
+ }
+ });
+
+ it('should handle null/undefined errors', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+
+ try {
+ adapter.handleError(null);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error).toBeNull();
+ }
+
+ try {
+ adapter.handleError(undefined);
+ fail('Expected handleError to throw');
+ } catch (error) {
+ expect(error).toBeUndefined();
+ }
+ });
+ });
+
+ describe('MongoDB Client Metadata', () => {
+ it('should not pass metadata to MongoClient by default', async () => {
+ const adapter = new MongoStorageAdapter({ uri: databaseURI });
+ await adapter.connect();
+ const driverInfo = adapter.client.s.options.driverInfo;
+ // Either driverInfo should be undefined, or it should not contain our custom metadata
+ if (driverInfo) {
+ expect(driverInfo.name).toBeUndefined();
+ }
+ await adapter.handleShutdown();
+ });
+
+ it('should pass custom metadata to MongoClient when configured', async () => {
+ const customMetadata = { name: 'MyParseServer', version: '1.0.0' };
+ const adapter = new MongoStorageAdapter({
+ uri: databaseURI,
+ mongoOptions: { clientMetadata: customMetadata }
+ });
+ await adapter.connect();
+ expect(adapter.client.s.options.driverInfo.name).toBe(customMetadata.name);
+ expect(adapter.client.s.options.driverInfo.version).toBe(customMetadata.version);
+ await adapter.handleShutdown();
});
});
});
diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js
index 6a6ba9f7b2..a3c61214a8 100644
--- a/spec/MongoTransform.spec.js
+++ b/spec/MongoTransform.spec.js
@@ -1,245 +1,398 @@
// These tests are unit tests designed to only test transform.js.
-"use strict";
+'use strict';
-let transform = require('../src/Adapters/Storage/Mongo/MongoTransform');
-let dd = require('deep-diff');
-let mongodb = require('mongodb');
+const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform');
+const dd = require('deep-diff');
+const mongodb = require('mongodb');
+const Utils = require('../lib/Utils');
describe('parseObjectToMongoObjectForCreate', () => {
- it('a basic number', (done) => {
- var input = {five: 5};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, {
- fields: {five: {type: 'Number'}}
+ it('a basic number', done => {
+ const input = { five: 5 };
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: { five: { type: 'Number' } },
});
jequal(input, output);
done();
});
- it('an object with null values', (done) => {
- var input = {objectWithNullValues: {isNull: null, notNull: 3}};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, {
- fields: {objectWithNullValues: {type: 'object'}}
+ it('an object with null values', done => {
+ const input = { objectWithNullValues: { isNull: null, notNull: 3 } };
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: { objectWithNullValues: { type: 'object' } },
});
jequal(input, output);
done();
});
- it('built-in timestamps', (done) => {
- var input = {
- createdAt: "2015-10-06T21:24:50.332Z",
- updatedAt: "2015-10-06T21:24:50.332Z"
+ it('built-in timestamps with date', done => {
+ const input = {
+ createdAt: '2015-10-06T21:24:50.332Z',
+ updatedAt: '2015-10-06T21:24:50.332Z',
};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
- expect(output._created_at instanceof Date).toBe(true);
- expect(output._updated_at instanceof Date).toBe(true);
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: {},
+ });
+ expect(Utils.isDate(output._created_at)).toBe(true);
+ expect(Utils.isDate(output._updated_at)).toBe(true);
done();
});
- it('array of pointers', (done) => {
- var pointer = {
+ it('array of pointers', done => {
+ const pointer = {
__type: 'Pointer',
objectId: 'myId',
className: 'Blah',
};
- var out = transform.parseObjectToMongoObjectForCreate(null, {pointers: [pointer]},{
- fields: {pointers: {type: 'Array'}}
- });
+ const out = transform.parseObjectToMongoObjectForCreate(
+ null,
+ { pointers: [pointer] },
+ {
+ fields: { pointers: { type: 'Array' } },
+ }
+ );
jequal([pointer], out.pointers);
done();
});
//TODO: object creation requests shouldn't be seeing __op delete, it makes no sense to
//have __op delete in a new object. Figure out what this should actually be testing.
- xit('a delete op', (done) => {
- var input = {deleteMe: {__op: 'Delete'}};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
+ xit('a delete op', done => {
+ const input = { deleteMe: { __op: 'Delete' } };
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: {},
+ });
jequal(output, {});
done();
});
it('Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm', done => {
- var input = {ACL: {'0123': {'read': true, 'write': true}}};
- expect(() => transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} })).toThrow();
+ const input = { ACL: { '0123': { read: true, write: true } } };
+ expect(() =>
+ transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} })
+ ).toThrow();
done();
});
- it('plain', (done) => {
- var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
- var out = transform.parseObjectToMongoObjectForCreate(null, {location: geoPoint},{
- fields: {location: {type: 'GeoPoint'}}
- });
- expect(out.location).toEqual([180, -180]);
+ it('parse geopoint to mongo', done => {
+ const lat = -45;
+ const lng = 45;
+ const geoPoint = { __type: 'GeoPoint', latitude: lat, longitude: lng };
+ const out = transform.parseObjectToMongoObjectForCreate(
+ null,
+ { location: geoPoint },
+ {
+ fields: { location: { type: 'GeoPoint' } },
+ }
+ );
+ expect(out.location).toEqual([lng, lat]);
done();
});
- it('in array', (done) => {
- var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
- var out = transform.parseObjectToMongoObjectForCreate(null, {locations: [geoPoint, geoPoint]},{
- fields: {locations: {type: 'Array'}}
- });
+ it('parse polygon to mongo', done => {
+ const lat1 = -45;
+ const lng1 = 45;
+ const lat2 = -55;
+ const lng2 = 55;
+ const lat3 = -65;
+ const lng3 = 65;
+ const polygon = {
+ __type: 'Polygon',
+ coordinates: [
+ [lat1, lng1],
+ [lat2, lng2],
+ [lat3, lng3],
+ ],
+ };
+ const out = transform.parseObjectToMongoObjectForCreate(
+ null,
+ { location: polygon },
+ {
+ fields: { location: { type: 'Polygon' } },
+ }
+ );
+ expect(out.location.coordinates).toEqual([
+ [
+ [lng1, lat1],
+ [lng2, lat2],
+ [lng3, lat3],
+ [lng1, lat1],
+ ],
+ ]);
+ done();
+ });
+
+ it('in array', done => {
+ const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 };
+ const out = transform.parseObjectToMongoObjectForCreate(
+ null,
+ { locations: [geoPoint, geoPoint] },
+ {
+ fields: { locations: { type: 'Array' } },
+ }
+ );
expect(out.locations).toEqual([geoPoint, geoPoint]);
done();
});
- it('in sub-object', (done) => {
- var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
- var out = transform.parseObjectToMongoObjectForCreate(null, { locations: { start: geoPoint }},{
- fields: {locations: {type: 'Object'}}
- });
+ it('in sub-object', done => {
+ const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 };
+ const out = transform.parseObjectToMongoObjectForCreate(
+ null,
+ { locations: { start: geoPoint } },
+ {
+ fields: { locations: { type: 'Object' } },
+ }
+ );
expect(out).toEqual({ locations: { start: geoPoint } });
done();
});
- it('objectId', (done) => {
- var out = transform.transformWhere(null, {objectId: 'foo'});
+ it('objectId', done => {
+ const out = transform.transformWhere(null, { objectId: 'foo' });
expect(out._id).toEqual('foo');
done();
});
- it('objectId in a list', (done) => {
- var input = {
- objectId: {'$in': ['one', 'two', 'three']},
+ it('objectId in a list', done => {
+ const input = {
+ objectId: { $in: ['one', 'two', 'three'] },
};
- var output = transform.transformWhere(null, input);
+ const output = transform.transformWhere(null, input);
jequal(input.objectId, output._id);
done();
});
- it('built-in timestamps', (done) => {
- var input = {createdAt: new Date(), updatedAt: new Date()};
- var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
+ it('built-in timestamps', done => {
+ const input = { createdAt: new Date(), updatedAt: new Date() };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {},
+ });
expect(typeof output.createdAt).toEqual('string');
expect(typeof output.updatedAt).toEqual('string');
done();
});
- it('pointer', (done) => {
- var input = {_p_userPointer: '_User$123'};
- var output = transform.mongoObjectToParseObject(null, input, {
+ it('pointer', done => {
+ const input = { _p_userPointer: '_User$123' };
+ const output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(typeof output.userPointer).toEqual('object');
- expect(output.userPointer).toEqual(
- {__type: 'Pointer', className: '_User', objectId: '123'}
- );
+ expect(output.userPointer).toEqual({
+ __type: 'Pointer',
+ className: '_User',
+ objectId: '123',
+ });
done();
});
- it('null pointer', (done) => {
- var input = {_p_userPointer: null};
- var output = transform.mongoObjectToParseObject(null, input, {
+ it('null pointer', done => {
+ const input = { _p_userPointer: null };
+ const output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(output.userPointer).toBeUndefined();
done();
});
- it('file', (done) => {
- var input = {picture: 'pic.jpg'};
- var output = transform.mongoObjectToParseObject(null, input, {
- fields: { picture: { type: 'File' }},
+ it('file', done => {
+ const input = { picture: 'pic.jpg' };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: { picture: { type: 'File' } },
});
expect(typeof output.picture).toEqual('object');
- expect(output.picture).toEqual({__type: 'File', name: 'pic.jpg'});
+ expect(output.picture).toEqual({ __type: 'File', name: 'pic.jpg' });
done();
});
- it('geopoint', (done) => {
- var input = {location: [180, -180]};
- var output = transform.mongoObjectToParseObject(null, input, {
- fields: { location: { type: 'GeoPoint' }},
+ it('mongo geopoint to parse', done => {
+ const lat = -45;
+ const lng = 45;
+ const input = { location: [lng, lat] };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: { location: { type: 'GeoPoint' } },
});
expect(typeof output.location).toEqual('object');
- expect(output.location).toEqual(
- {__type: 'GeoPoint', longitude: 180, latitude: -180}
- );
+ expect(output.location).toEqual({
+ __type: 'GeoPoint',
+ latitude: lat,
+ longitude: lng,
+ });
done();
});
- it('nested array', (done) => {
- var input = {arr: [{_testKey: 'testValue' }]};
- var output = transform.mongoObjectToParseObject(null, input, {
+ it('mongo polygon to parse', done => {
+ const lat = -45;
+ const lng = 45;
+ // Mongo stores polygon in WGS84 lng/lat
+ const input = {
+ location: {
+ type: 'Polygon',
+ coordinates: [
+ [
+ [lat, lng],
+ [lat, lng],
+ ],
+ ],
+ },
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: { location: { type: 'Polygon' } },
+ });
+ expect(typeof output.location).toEqual('object');
+ expect(output.location).toEqual({
+ __type: 'Polygon',
+ coordinates: [
+ [lng, lat],
+ [lng, lat],
+ ],
+ });
+ done();
+ });
+
+ it('bytes', done => {
+ const input = { binaryData: 'aGVsbG8gd29ybGQ=' };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: { binaryData: { type: 'Bytes' } },
+ });
+ expect(typeof output.binaryData).toEqual('object');
+ expect(output.binaryData).toEqual({
+ __type: 'Bytes',
+ base64: 'aGVsbG8gd29ybGQ=',
+ });
+ done();
+ });
+
+ it('nested array', done => {
+ const input = { arr: [{ _testKey: 'testValue' }] };
+ const output = transform.mongoObjectToParseObject(null, input, {
fields: { arr: { type: 'Array' } },
});
expect(Array.isArray(output.arr)).toEqual(true);
- expect(output.arr).toEqual([{ _testKey: 'testValue'}]);
+ expect(output.arr).toEqual([{ _testKey: 'testValue' }]);
done();
});
it('untransforms objects containing nested special keys', done => {
- let input = {array: [{
- _id: "Test ID",
- _hashed_password: "I Don't know why you would name a key this, but if you do it should work",
- _tombstone: {
- _updated_at: "I'm sure people will nest keys like this",
- _acl: 7,
- _id: { someString: "str", someNumber: 7},
- regularKey: { moreContents: [1, 2, 3] },
- },
- regularKey: "some data",
- }]}
- let output = transform.mongoObjectToParseObject(null, input, {
- fields: { array: { type: 'Array' }},
+ const input = {
+ array: [
+ {
+ _id: 'Test ID',
+ _hashed_password:
+ "I Don't know why you would name a key this, but if you do it should work",
+ _tombstone: {
+ _updated_at: "I'm sure people will nest keys like this",
+ _acl: 7,
+ _id: { someString: 'str', someNumber: 7 },
+ regularKey: { moreContents: [1, 2, 3] },
+ },
+ regularKey: 'some data',
+ },
+ ],
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: { array: { type: 'Array' } },
});
expect(dd(output, input)).toEqual(undefined);
done();
});
- it('changes new pointer key', (done) => {
- var input = {
- somePointer: {__type: 'Pointer', className: 'Micro', objectId: 'oft'}
+ it('changes new pointer key', done => {
+ const input = {
+ somePointer: { __type: 'Pointer', className: 'Micro', objectId: 'oft' },
};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, {
- fields: {somePointer: {type: 'Pointer'}}
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: { somePointer: { type: 'Pointer' } },
});
expect(typeof output._p_somePointer).toEqual('string');
expect(output._p_somePointer).toEqual('Micro$oft');
done();
});
- it('changes existing pointer keys', (done) => {
- var input = {
- userPointer: {__type: 'Pointer', className: '_User', objectId: 'qwerty'}
+ it('changes existing pointer keys', done => {
+ const input = {
+ userPointer: {
+ __type: 'Pointer',
+ className: '_User',
+ objectId: 'qwerty',
+ },
};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, {
- fields: {userPointer: {type: 'Pointer'}}
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: { userPointer: { type: 'Pointer' } },
});
expect(typeof output._p_userPointer).toEqual('string');
expect(output._p_userPointer).toEqual('_User$qwerty');
done();
});
- it('writes the old ACL format in addition to rperm and wperm', (done) => {
- var input = {
+ it('writes the old ACL format in addition to rperm and wperm on create', done => {
+ const input = {
_rperm: ['*'],
_wperm: ['Kevin'],
};
- var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: {},
+ });
expect(typeof output._acl).toEqual('object');
- expect(output._acl["Kevin"].w).toBeTruthy();
- expect(output._acl["Kevin"].r).toBeUndefined();
+ expect(output._acl['Kevin'].w).toBeTruthy();
+ expect(output._acl['Kevin'].r).toBeUndefined();
+ expect(output._rperm).toEqual(input._rperm);
+ expect(output._wperm).toEqual(input._wperm);
done();
- })
+ });
- it('untransforms from _rperm and _wperm to ACL', (done) => {
- var input = {
- _rperm: ["*"],
- _wperm: ["Kevin"]
+ it('removes Relation types', done => {
+ const input = {
+ aRelation: { __type: 'Relation', className: 'Stuff' },
};
- var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
+ const output = transform.parseObjectToMongoObjectForCreate(null, input, {
+ fields: {
+ aRelation: { __type: 'Relation', className: 'Stuff' },
+ },
+ });
+ expect(output).toEqual({});
+ done();
+ });
+
+ it('writes the old ACL format in addition to rperm and wperm on update', done => {
+ const input = {
+ _rperm: ['*'],
+ _wperm: ['Kevin'],
+ };
+
+ const output = transform.transformUpdate(null, input, { fields: {} });
+ const set = output.$set;
+ expect(typeof set).toEqual('object');
+ expect(typeof set._acl).toEqual('object');
+ expect(set._acl['Kevin'].w).toBeTruthy();
+ expect(set._acl['Kevin'].r).toBeUndefined();
+ expect(set._rperm).toEqual(input._rperm);
+ expect(set._wperm).toEqual(input._wperm);
+ done();
+ });
+
+ it('untransforms from _rperm and _wperm to ACL', done => {
+ const input = {
+ _rperm: ['*'],
+ _wperm: ['Kevin'],
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {},
+ });
expect(output._rperm).toEqual(['*']);
expect(output._wperm).toEqual(['Kevin']);
- expect(output.ACL).toBeUndefined()
+ expect(output.ACL).toBeUndefined();
done();
});
- it('untransforms mongodb number types', (done) =>Â {
- var input = {
+ it('untransforms mongodb number types', done => {
+ const input = {
long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
- double: new mongodb.Double(Number.MAX_VALUE)
- }
- var output = transform.mongoObjectToParseObject(null, input, {
+ double: new mongodb.Double(Number.MAX_VALUE),
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
fields: {
long: { type: 'Number' },
double: { type: 'Number' },
@@ -250,4 +403,294 @@ describe('parseObjectToMongoObjectForCreate', () => {
done();
});
+ it('Date object where iso attribute is of type Date', done => {
+ const input = {
+ ts: { __type: 'Date', iso: new Date('2017-01-18T00:00:00.000Z') },
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {
+ ts: { type: 'Date' },
+ },
+ });
+ expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z');
+ done();
+ });
+
+ it('Date object where iso attribute is of type String', done => {
+ const input = {
+ ts: { __type: 'Date', iso: '2017-01-18T00:00:00.000Z' },
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {
+ ts: { type: 'Date' },
+ },
+ });
+ expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z');
+ done();
+ });
+
+ it('object with undefined nested values', () => {
+ const input = {
+ _id: 'vQHyinCW1l',
+ urls: { firstUrl: 'https://', secondUrl: undefined },
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {
+ urls: { type: 'Object' },
+ },
+ });
+ expect(output.urls).toEqual({
+ firstUrl: 'https://',
+ secondUrl: undefined,
+ });
+ });
+
+ it('undefined objects', () => {
+ const input = {
+ _id: 'vQHyinCW1l',
+ urls: undefined,
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {
+ urls: { type: 'Object' },
+ },
+ });
+ expect(output.urls).toBeUndefined();
+ });
+
+ it('$regex in $all list', done => {
+ const input = {
+ arrayField: {
+ $all: [{ $regex: '^\\Qone\\E' }, { $regex: '^\\Qtwo\\E' }, { $regex: '^\\Qthree\\E' }],
+ },
+ };
+ const outputValue = {
+ arrayField: { $all: [/^\Qone\E/, /^\Qtwo\E/, /^\Qthree\E/] },
+ };
+
+ const output = transform.transformWhere(null, input);
+ jequal(outputValue.arrayField, output.arrayField);
+ done();
+ });
+
+ it('$regex in $all list must be { $regex: "string" }', done => {
+ const input = {
+ arrayField: { $all: [{ $regex: 1 }] },
+ };
+
+ expect(() => {
+ transform.transformWhere(null, input);
+ }).toThrow();
+ done();
+ });
+
+ it('all values in $all must be $regex (start with string) or non $regex (start with string)', done => {
+ const input = {
+ arrayField: {
+ $all: [{ $regex: '^\\Qone\\E' }, { $unknown: '^\\Qtwo\\E' }],
+ },
+ };
+
+ expect(() => {
+ transform.transformWhere(null, input);
+ }).toThrow();
+ done();
+ });
+
+ it('ignores User authData field in DB so it can be synthesized in code', done => {
+ const input = {
+ _id: '123',
+ _auth_data_acme: { id: 'abc' },
+ authData: null,
+ };
+ const output = transform.mongoObjectToParseObject('_User', input, {
+ fields: {},
+ });
+ expect(output.authData.acme.id).toBe('abc');
+ done();
+ });
+
+ it('can set authData when not User class', done => {
+ const input = {
+ _id: '123',
+ authData: 'random',
+ };
+ const output = transform.mongoObjectToParseObject('TestObject', input, {
+ fields: {},
+ });
+ expect(output.authData).toBe('random');
+ done();
+ });
+
+ it('should only transform authData.provider.id for _User class', () => {
+ // Test that for _User class, authData.facebook.id is transformed
+ const userInput = {
+ 'authData.facebook.id': '10000000000000001',
+ };
+ const userOutput = transform.transformWhere('_User', userInput, { fields: {} });
+ expect(userOutput['_auth_data_facebook.id']).toBe('10000000000000001');
+
+ // Test that for non-User classes, authData.facebook.id is NOT transformed
+ const customInput = {
+ 'authData.facebook.id': '10000000000000001',
+ };
+ const customOutput = transform.transformWhere('SpamAlerts', customInput, { fields: {} });
+ expect(customOutput['authData.facebook.id']).toBe('10000000000000001');
+ expect(customOutput['_auth_data_facebook.id']).toBeUndefined();
+ });
+});
+
+it('cannot have a custom field name beginning with underscore', done => {
+ const input = {
+ _id: '123',
+ _thisFieldNameIs: 'invalid',
+ };
+ try {
+ transform.mongoObjectToParseObject('TestObject', input, {
+ fields: {},
+ });
+ } catch (e) {
+ expect(e).toBeDefined();
+ }
+ done();
+});
+
+describe('transformUpdate', () => {
+ it('removes Relation types', done => {
+ const input = {
+ aRelation: { __type: 'Relation', className: 'Stuff' },
+ };
+ const output = transform.transformUpdate(null, input, {
+ fields: {
+ aRelation: { __type: 'Relation', className: 'Stuff' },
+ },
+ });
+ expect(output).toEqual({});
+ done();
+ });
+});
+
+describe('transformConstraint', () => {
+ describe('$relativeTime', () => {
+ it('should error on $eq, $ne, and $exists', () => {
+ expect(() => {
+ transform.transformConstraint({
+ $eq: {
+ ttl: {
+ $relativeTime: '12 days ago',
+ },
+ },
+ });
+ }).toThrow();
+
+ expect(() => {
+ transform.transformConstraint({
+ $ne: {
+ ttl: {
+ $relativeTime: '12 days ago',
+ },
+ },
+ });
+ }).toThrow();
+
+ expect(() => {
+ transform.transformConstraint({
+ $exists: {
+ $relativeTime: '12 days ago',
+ },
+ });
+ }).toThrow();
+ });
+ });
+});
+
+describe('relativeTimeToDate', () => {
+ const now = new Date('2017-09-26T13:28:16.617Z');
+
+ describe('In the future', () => {
+ it('should parse valid natural time', () => {
+ const text = 'in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds';
+ const { result, status, info } = Utils.relativeTimeToDate(text, now);
+ expect(result.toISOString()).toBe('2018-10-22T23:52:46.617Z');
+ expect(status).toBe('success');
+ expect(info).toBe('future');
+ });
+ });
+
+ describe('In the past', () => {
+ it('should parse valid natural time', () => {
+ const text = '2 days 12 hours 1 minute 12 seconds ago';
+ const { result, status, info } = Utils.relativeTimeToDate(text, now);
+ expect(result.toISOString()).toBe('2017-09-24T01:27:04.617Z');
+ expect(status).toBe('success');
+ expect(info).toBe('past');
+ });
+ });
+
+ describe('From now', () => {
+ it('should equal current time', () => {
+ const text = 'now';
+ const { result, status, info } = Utils.relativeTimeToDate(text, now);
+ expect(result.toISOString()).toBe('2017-09-26T13:28:16.617Z');
+ expect(status).toBe('success');
+ expect(info).toBe('present');
+ });
+ });
+
+ describe('Error cases', () => {
+ it('should error if string is completely gibberish', () => {
+ expect(Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')).toEqual({
+ status: 'error',
+ info: "Time should either start with 'in' or end with 'ago'",
+ });
+ });
+
+ it('should error if string contains neither `ago` nor `in`', () => {
+ expect(Utils.relativeTimeToDate('12 hours 1 minute')).toEqual({
+ status: 'error',
+ info: "Time should either start with 'in' or end with 'ago'",
+ });
+ });
+
+ it('should error if there are missing units or numbers', () => {
+ expect(Utils.relativeTimeToDate('in 12 hours 1')).toEqual({
+ status: 'error',
+ info: 'Invalid time string. Dangling unit or number.',
+ });
+
+ expect(Utils.relativeTimeToDate('12 hours minute ago')).toEqual({
+ status: 'error',
+ info: 'Invalid time string. Dangling unit or number.',
+ });
+ });
+
+ it('should error on floating point numbers', () => {
+ expect(Utils.relativeTimeToDate('in 12.3 hours')).toEqual({
+ status: 'error',
+ info: "'12.3' is not an integer.",
+ });
+ });
+
+ it('should error if numbers are invalid', () => {
+ expect(Utils.relativeTimeToDate('12 hours 123a minute ago')).toEqual({
+ status: 'error',
+ info: "'123a' is not an integer.",
+ });
+ });
+
+ it('should error on invalid interval units', () => {
+ expect(Utils.relativeTimeToDate('4 score 7 years ago')).toEqual({
+ status: 'error',
+ info: "Invalid interval: 'score'",
+ });
+ });
+
+ it("should error when string contains 'ago' and 'in'", () => {
+ expect(Utils.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({
+ status: 'error',
+ info: "Time cannot have both 'in' and 'ago'",
+ });
+ });
+ });
+
});
diff --git a/spec/NullCacheAdapter.spec.js b/spec/NullCacheAdapter.spec.js
new file mode 100644
index 0000000000..f5d5e508f4
--- /dev/null
+++ b/spec/NullCacheAdapter.spec.js
@@ -0,0 +1,32 @@
+const NullCacheAdapter = require('../lib/Adapters/Cache/NullCacheAdapter').default;
+
+describe('NullCacheAdapter', function () {
+ const KEY = 'hello';
+ const VALUE = 'world';
+
+ it('should expose promisifyed methods', done => {
+ const cache = new NullCacheAdapter({
+ ttl: NaN,
+ });
+
+ // Verify all methods return promises.
+ Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => {
+ done();
+ });
+ });
+
+ it('should get/set/clear', done => {
+ const cache = new NullCacheAdapter({
+ ttl: NaN,
+ });
+
+ cache
+ .put(KEY, VALUE)
+ .then(() => cache.get(KEY))
+ .then(value => expect(value).toEqual(null))
+ .then(() => cache.clear())
+ .then(() => cache.get(KEY))
+ .then(value => expect(value).toEqual(null))
+ .then(done);
+ });
+});
diff --git a/spec/OAuth.spec.js b/spec/OAuth.spec.js
deleted file mode 100644
index 60cb7d9189..0000000000
--- a/spec/OAuth.spec.js
+++ /dev/null
@@ -1,324 +0,0 @@
-var OAuth = require("../src/authDataManager/OAuth1Client");
-var request = require('request');
-var Config = require("../src/Config");
-var defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
-
-describe('OAuth', function() {
- it("Nonce should have right length", (done) => {
- jequal(OAuth.nonce().length, 30);
- done();
- });
-
- it("Should properly build parameter string", (done) => {
- var string = OAuth.buildParameterString({c:1, a:2, b:3})
- jequal(string, "a=2&b=3&c=1");
- done();
- });
-
- it("Should properly build empty parameter string", (done) => {
- var string = OAuth.buildParameterString()
- jequal(string, "");
- done();
- });
-
- it("Should properly build signature string", (done) => {
- var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
- jequal(string, "GET&http%3A%2F%2Fdummy.com&");
- done();
- });
-
- it("Should properly generate request signature", (done) => {
- var request = {
- host: "dummy.com",
- path: "path"
- };
-
- var oauth_params = {
- oauth_timestamp: 123450000,
- oauth_nonce: "AAAAAAAAAAAAAAAAA",
- oauth_consumer_key: "hello",
- oauth_token: "token"
- };
-
- var consumer_secret = "world";
- var auth_token_secret = "secret";
- request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
- jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
- done();
- });
-
- it("Should properly build request", (done) => {
- var options = {
- host: "dummy.com",
- consumer_key: "hello",
- consumer_secret: "world",
- auth_token: "token",
- auth_token_secret: "secret",
- // Custom oauth params for tests
- oauth_params: {
- oauth_timestamp: 123450000,
- oauth_nonce: "AAAAAAAAAAAAAAAAA"
- }
- };
- var path = "path";
- var method = "get";
-
- var oauthClient = new OAuth(options);
- var req = oauthClient.buildRequest(method, path, {"query": "param"});
-
- jequal(req.host, options.host);
- jequal(req.path, "/"+path+"?query=param");
- jequal(req.method, "GET");
- jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
- jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
- done();
- });
-
-
- function validateCannotAuthenticateError(data, done) {
- jequal(typeof data, "object");
- jequal(typeof data.errors, "object");
- var errors = data.errors;
- jequal(typeof errors[0], "object");
- // Cannot authenticate error
- jequal(errors[0].code, 32);
- done();
- }
-
- it("Should fail a GET request", (done) => {
- var options = {
- host: "api.twitter.com",
- consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
- consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- };
- var path = "/1.1/help/configuration.json";
- var params = {"lang": "en"};
- var oauthClient = new OAuth(options);
- oauthClient.get(path, params).then(function(data){
- validateCannotAuthenticateError(data, done);
- })
- });
-
- it("Should fail a POST request", (done) => {
- var options = {
- host: "api.twitter.com",
- consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
- consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- };
- var body = {
- lang: "en"
- };
- var path = "/1.1/account/settings.json";
-
- var oauthClient = new OAuth(options);
- oauthClient.post(path, null, body).then(function(data){
- validateCannotAuthenticateError(data, done);
- })
- });
-
- it("Should fail a request", (done) => {
- var options = {
- host: "localhost",
- consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
- consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- };
- var body = {
- lang: "en"
- };
- var path = "/";
-
- var oauthClient = new OAuth(options);
- oauthClient.post(path, null, body).then(function(data){
- jequal(false, true);
- done();
- }).catch(function(){
- jequal(true, true);
- done();
- })
- });
-
- ["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter"].map(function(providerName){
- it("Should validate structure of "+providerName, (done) => {
- var provider = require("../src/authDataManager/"+providerName);
- jequal(typeof provider.validateAuthData, "function");
- jequal(typeof provider.validateAppId, "function");
- jequal(provider.validateAuthData({}, {}).constructor, Promise.prototype.constructor);
- jequal(provider.validateAppId("app", "key", {}).constructor, Promise.prototype.constructor);
- done();
- });
- });
-
- var getMockMyOauthProvider = function() {
- return {
- authData: {
- id: "12345",
- access_token: "12345",
- expiration_date: new Date().toJSON(),
- },
- shouldError: false,
- loggedOut: false,
- synchronizedUserId: null,
- synchronizedAuthToken: null,
- synchronizedExpiration: null,
-
- authenticate: function(options) {
- if (this.shouldError) {
- options.error(this, "An error occurred");
- } else if (this.shouldCancel) {
- options.error(this, null);
- } else {
- options.success(this, this.authData);
- }
- },
- restoreAuthentication: function(authData) {
- if (!authData) {
- this.synchronizedUserId = null;
- this.synchronizedAuthToken = null;
- this.synchronizedExpiration = null;
- return true;
- }
- this.synchronizedUserId = authData.id;
- this.synchronizedAuthToken = authData.access_token;
- this.synchronizedExpiration = authData.expiration_date;
- return true;
- },
- getAuthType: function() {
- return "myoauth";
- },
- deauthenticate: function() {
- this.loggedOut = true;
- this.restoreAuthentication(null);
- }
- };
- };
-
- var ExtendedUser = Parse.User.extend({
- extended: function() {
- return true;
- }
- });
-
- var createOAuthUser = function(callback) {
- var jsonBody = {
- authData: {
- myoauth: getMockMyOauthProvider().authData
- }
- };
-
- var options = {
- headers: {'X-Parse-Application-Id': 'test',
- 'X-Parse-REST-API-Key': 'rest',
- 'X-Parse-Installation-Id': 'yolo',
- 'Content-Type': 'application/json' },
- url: 'http://localhost:8378/1/users',
- body: JSON.stringify(jsonBody)
- };
-
- return request.post(options, callback);
- }
-
- it_exclude_dbs(['postgres'])("should create user with REST API", done => {
- createOAuthUser((error, response, body) => {
- expect(error).toBe(null);
- var b = JSON.parse(body);
- ok(b.sessionToken);
- expect(b.objectId).not.toBeNull();
- expect(b.objectId).not.toBeUndefined();
- var sessionToken = b.sessionToken;
- var q = new Parse.Query("_Session");
- q.equalTo('sessionToken', sessionToken);
- q.first({useMasterKey: true}).then((res) =>Â {
- expect(res.get("installationId")).toEqual('yolo');
- done();
- }).fail((err) => {
- fail('should not fail fetching the session');
- done();
- })
- });
- });
-
- it_exclude_dbs(['postgres'])("should only create a single user with REST API", (done) => {
- var objectId;
- createOAuthUser((error, response, body) => {
- expect(error).toBe(null);
- var b = JSON.parse(body);
- expect(b.objectId).not.toBeNull();
- expect(b.objectId).not.toBeUndefined();
- objectId = b.objectId;
-
- createOAuthUser((error, response, body) => {
- expect(error).toBe(null);
- var b = JSON.parse(body);
- expect(b.objectId).not.toBeNull();
- expect(b.objectId).not.toBeUndefined();
- expect(b.objectId).toBe(objectId);
- done();
- });
- });
- });
-
- it_exclude_dbs(['postgres'])("unlink and link with custom provider", (done) => {
- var provider = getMockMyOauthProvider();
- Parse.User._registerAuthenticationProvider(provider);
- Parse.User._logInWith("myoauth", {
- success: function(model) {
- ok(model instanceof Parse.User, "Model should be a Parse.User");
- strictEqual(Parse.User.current(), model);
- ok(model.extended(), "Should have used the subclass.");
- strictEqual(provider.authData.id, provider.synchronizedUserId);
- strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
- strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
- ok(model._isLinked("myoauth"), "User should be linked to myoauth");
-
- model._unlinkFrom("myoauth", {
- success: function(model) {
-
- ok(!model._isLinked("myoauth"),
- "User should not be linked to myoauth");
- ok(!provider.synchronizedUserId, "User id should be cleared");
- ok(!provider.synchronizedAuthToken, "Auth token should be cleared");
- ok(!provider.synchronizedExpiration,
- "Expiration should be cleared");
- // make sure the auth data is properly deleted
- var config = new Config(Parse.applicationId);
- config.database.adapter.find('_User', {
- fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation),
- }, { objectId: model.id }, {})
- .then(res => {
- expect(res.length).toBe(1);
- expect(res[0]._auth_data_myoauth).toBeUndefined();
- expect(res[0]._auth_data_myoauth).not.toBeNull();
-
- model._linkWith("myoauth", {
- success: function(model) {
- ok(provider.synchronizedUserId, "User id should have a value");
- ok(provider.synchronizedAuthToken,
- "Auth token should have a value");
- ok(provider.synchronizedExpiration,
- "Expiration should have a value");
- ok(model._isLinked("myoauth"),
- "User should be linked to myoauth");
- done();
- },
- error: function(model, error) {
- ok(false, "linking again should succeed");
- done();
- }
- });
- });
- },
- error: function(model, error) {
- ok(false, "unlinking should succeed");
- done();
- }
- });
- },
- error: function(model, error) {
- ok(false, "linking should have worked");
- done();
- }
- });
- });
-
-
-})
diff --git a/spec/OAuth1.spec.js b/spec/OAuth1.spec.js
new file mode 100644
index 0000000000..34dc8b6925
--- /dev/null
+++ b/spec/OAuth1.spec.js
@@ -0,0 +1,162 @@
+const OAuth = require('../lib/Adapters/Auth/OAuth1Client');
+
+describe('OAuth', function () {
+ it('Nonce should have right length', done => {
+ jequal(OAuth.nonce().length, 30);
+ done();
+ });
+
+ it('Should properly build parameter string', done => {
+ const string = OAuth.buildParameterString({ c: 1, a: 2, b: 3 });
+ jequal(string, 'a=2&b=3&c=1');
+ done();
+ });
+
+ it('Should properly build empty parameter string', done => {
+ const string = OAuth.buildParameterString();
+ jequal(string, '');
+ done();
+ });
+
+ it('Should properly build signature string', done => {
+ const string = OAuth.buildSignatureString('get', 'http://dummy.com', '');
+ jequal(string, 'GET&http%3A%2F%2Fdummy.com&');
+ done();
+ });
+
+ it('Should properly generate request signature', done => {
+ let request = {
+ host: 'dummy.com',
+ path: 'path',
+ };
+
+ const oauth_params = {
+ oauth_timestamp: 123450000,
+ oauth_nonce: 'AAAAAAAAAAAAAAAAA',
+ oauth_consumer_key: 'hello',
+ oauth_token: 'token',
+ };
+
+ const consumer_secret = 'world';
+ const auth_token_secret = 'secret';
+ request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
+ jequal(
+ request.headers['Authorization'],
+ 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"'
+ );
+ done();
+ });
+
+ it('Should properly build request', done => {
+ const options = {
+ host: 'dummy.com',
+ consumer_key: 'hello',
+ consumer_secret: 'world',
+ auth_token: 'token',
+ auth_token_secret: 'secret',
+ // Custom oauth params for tests
+ oauth_params: {
+ oauth_timestamp: 123450000,
+ oauth_nonce: 'AAAAAAAAAAAAAAAAA',
+ },
+ };
+ const path = 'path';
+ const method = 'get';
+
+ const oauthClient = new OAuth(options);
+ const req = oauthClient.buildRequest(method, path, { query: 'param' });
+
+ jequal(req.host, options.host);
+ jequal(req.path, '/' + path + '?query=param');
+ jequal(req.method, 'GET');
+ jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
+ jequal(
+ req.headers['Authorization'],
+ 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"'
+ );
+ done();
+ });
+
+ function validateCannotAuthenticateError(data, done) {
+ jequal(typeof data, 'object');
+ jequal(typeof data.errors, 'object');
+ const errors = data.errors;
+ jequal(typeof errors[0], 'object');
+ // Cannot authenticate error
+ jequal(errors[0].code, 32);
+ done();
+ }
+
+ xit('GET request for a resource that requires OAuth should fail with invalid credentials', done => {
+ /*
+ This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication.
+ Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates.
+ */
+ const options = {
+ host: 'api.twitter.com',
+ consumer_key: 'invalid_consumer_key',
+ consumer_secret: 'invalid_consumer_secret',
+ };
+ const path = '/1.1/favorites/list.json';
+ const params = { lang: 'en' };
+ const oauthClient = new OAuth(options);
+ oauthClient.get(path, params).then(function (data) {
+ validateCannotAuthenticateError(data, done);
+ });
+ });
+
+ xit('POST request for a resource that requires OAuth should fail with invalid credentials', done => {
+ /*
+ This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication.
+ Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates.
+ */
+ const options = {
+ host: 'api.twitter.com',
+ consumer_key: 'invalid_consumer_key',
+ consumer_secret: 'invalid_consumer_secret',
+ };
+ const body = {
+ lang: 'en',
+ };
+ const path = '/1.1/account/settings.json';
+
+ const oauthClient = new OAuth(options);
+ oauthClient.post(path, null, body).then(function (data) {
+ validateCannotAuthenticateError(data, done);
+ });
+ });
+
+ it('Should fail a request', done => {
+ const options = {
+ host: 'localhost',
+ consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
+ consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ };
+ const body = {
+ lang: 'en',
+ };
+ const path = '/';
+
+ const oauthClient = new OAuth(options);
+ oauthClient
+ .post(path, null, body)
+ .then(function () {
+ jequal(false, true);
+ done();
+ })
+ .catch(function () {
+ jequal(true, true);
+ done();
+ });
+ });
+
+ it('Should fail with missing options', done => {
+ const options = undefined;
+ try {
+ new OAuth(options);
+ } catch (error) {
+ jequal(error.message, 'No options passed to OAuth');
+ done();
+ }
+ });
+});
diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js
new file mode 100644
index 0000000000..68dfb0b17b
--- /dev/null
+++ b/spec/PagesRouter.spec.js
@@ -0,0 +1,1683 @@
+'use strict';
+
+const request = require('../lib/request');
+const path = require('path');
+const fs = require('fs').promises;
+const mustache = require('mustache');
+const Utils = require('../lib/Utils');
+const { Page } = require('../lib/Page');
+const Config = require('../lib/Config');
+const Definitions = require('../lib/Options/Definitions');
+const UserController = require('../lib/Controllers/UserController').UserController;
+const {
+ PagesRouter,
+ pages,
+ pageParams,
+ pageParamHeaderPrefix,
+} = require('../lib/Routers/PagesRouter');
+
+describe('Pages Router', () => {
+ describe('basic request', () => {
+ let config;
+
+ beforeEach(async () => {
+ config = {
+ appId: 'test',
+ appName: 'exampleAppname',
+ publicServerURL: 'http://localhost:8378/1',
+ pages: {},
+ };
+ await reconfigureServer(config);
+ });
+
+ it('responds with file content on direct page request', async () => {
+ const urls = [
+ 'http://localhost:8378/1/apps/email_verification_link_invalid.html',
+ 'http://localhost:8378/1/apps/choose_password?appId=test',
+ 'http://localhost:8378/1/apps/email_verification_success.html',
+ 'http://localhost:8378/1/apps/password_reset_success.html',
+ 'http://localhost:8378/1/apps/custom_json.html',
+ ];
+ for (const url of urls) {
+ const response = await request({ url }).catch(e => e);
+ expect(response.status).toBe(200);
+ }
+ });
+
+ it('can load file from custom pages path', async () => {
+ config.pages.pagesPath = './public';
+ await reconfigureServer(config);
+
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/email_verification_link_invalid.html',
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ });
+
+ it('can load file from custom pages endpoint', async () => {
+ config.pages.pagesEndpoint = 'pages';
+ await reconfigureServer(config);
+
+ const response = await request({
+ url: `http://localhost:8378/1/pages/email_verification_link_invalid.html`,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ });
+
+ it('responds with 404 if publicServerURL is not configured', async () => {
+ await reconfigureServer({
+ appName: 'unused',
+ });
+ const urls = [
+ 'http://localhost:8378/1/apps/test/verify_email',
+ 'http://localhost:8378/1/apps/choose_password?appId=test',
+ 'http://localhost:8378/1/apps/test/request_password_reset',
+ ];
+ for (const url of urls) {
+ const response = await request({ url }).catch(e => e);
+ expect(response.status).toBe(404);
+ }
+ });
+
+ it('responds with 403 access denied with invalid appId', async () => {
+ const reqs = [
+ { url: 'http://localhost:8378/1/apps/invalid/verify_email', method: 'GET' },
+ { url: 'http://localhost:8378/1/apps/choose_password?id=invalid', method: 'GET' },
+ { url: 'http://localhost:8378/1/apps/invalid/request_password_reset', method: 'GET' },
+ { url: 'http://localhost:8378/1/apps/invalid/request_password_reset', method: 'POST' },
+ { url: 'http://localhost:8378/1/apps/invalid/resend_verification_email', method: 'POST' },
+ ];
+ for (const req of reqs) {
+ const response = await request(req).catch(e => e);
+ expect(response.status).toBe(403);
+ }
+ });
+ });
+
+ describe('AJAX requests', () => {
+ beforeEach(async () => {
+ await reconfigureServer({
+ appName: 'exampleAppname',
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ });
+
+ it('request_password_reset: responds with AJAX success', async () => {
+ spyOn(UserController.prototype, 'updatePassword').and.callFake(() => Promise.resolve());
+ const res = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/apps/test/request_password_reset',
+ body: `new_password=user1&token=43634643`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ followRedirects: false,
+ }).catch(e => e);
+ expect(res.status).toBe(200);
+ expect(res.text).toEqual('"Password successfully reset"');
+ });
+
+ it('request_password_reset: responds with AJAX error on missing password', async () => {
+ try {
+ await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/apps/test/request_password_reset',
+ body: `new_password=&token=132414`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ followRedirects: false,
+ });
+ } catch (error) {
+ expect(error.status).not.toBe(302);
+ expect(error.text).toEqual('{"code":201,"error":"Missing password"}');
+ }
+ });
+
+ it('request_password_reset: responds with AJAX error on missing token', async () => {
+ try {
+ await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/apps/test/request_password_reset',
+ body: `new_password=user1&token=`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ followRedirects: false,
+ });
+ } catch (error) {
+ expect(error.status).not.toBe(302);
+ expect(error.text).toEqual('{"code":-1,"error":"Missing token"}');
+ }
+ });
+ });
+
+ describe('pages', () => {
+ let router = new PagesRouter();
+ let req;
+ let config;
+ let goToPage;
+ let pageResponse;
+ let redirectResponse;
+ let readFile;
+ let exampleLocale;
+
+ const fillPlaceholders = (text, fill) => text.replace(/({{2,3}.*?}{2,3})/g, fill);
+ async function reconfigureServerWithPagesConfig(pagesConfig) {
+ config.pages = pagesConfig;
+ await reconfigureServer(config);
+ }
+
+ beforeEach(async () => {
+ router = new PagesRouter();
+ readFile = spyOn(fs, 'readFile').and.callThrough();
+ goToPage = spyOn(PagesRouter.prototype, 'goToPage').and.callThrough();
+ pageResponse = spyOn(PagesRouter.prototype, 'pageResponse').and.callThrough();
+ redirectResponse = spyOn(PagesRouter.prototype, 'redirectResponse').and.callThrough();
+ exampleLocale = 'de-AT';
+ config = {
+ appId: 'test',
+ appName: 'ExampleAppName',
+ verifyUserEmails: true,
+ emailAdapter: {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ },
+ publicServerURL: 'http://localhost:8378/1',
+ pages: {
+ enableLocalization: true,
+ customUrls: {},
+ },
+ };
+ req = {
+ method: 'GET',
+ config,
+ query: {
+ locale: exampleLocale,
+ },
+ };
+ });
+
+ describe('server options', () => {
+ it('uses default configuration when none is set', async () => {
+ await reconfigureServerWithPagesConfig({});
+ expect(Config.get(Parse.applicationId).pages.enableLocalization).toBe(
+ Definitions.PagesOptions.enableLocalization.default
+ );
+ expect(Config.get(Parse.applicationId).pages.localizationJsonPath).toBe(
+ Definitions.PagesOptions.localizationJsonPath.default
+ );
+ expect(Config.get(Parse.applicationId).pages.localizationFallbackLocale).toBe(
+ Definitions.PagesOptions.localizationFallbackLocale.default
+ );
+ expect(Config.get(Parse.applicationId).pages.placeholders).toBe(
+ Definitions.PagesOptions.placeholders.default
+ );
+ expect(Config.get(Parse.applicationId).pages.forceRedirect).toBe(
+ Definitions.PagesOptions.forceRedirect.default
+ );
+ expect(Config.get(Parse.applicationId).pages.pagesPath).toBeUndefined();
+ expect(Config.get(Parse.applicationId).pages.pagesEndpoint).toBe(
+ Definitions.PagesOptions.pagesEndpoint.default
+ );
+ expect(Config.get(Parse.applicationId).pages.customUrls).toBe(
+ Definitions.PagesOptions.customUrls.default
+ );
+ expect(Config.get(Parse.applicationId).pages.customRoutes).toBe(
+ Definitions.PagesOptions.customRoutes.default
+ );
+ });
+
+ it('throws on invalid configuration', async () => {
+ const options = [
+ [],
+ 'a',
+ 0,
+ true,
+ { enableLocalization: 'a' },
+ { enableLocalization: 0 },
+ { enableLocalization: {} },
+ { enableLocalization: [] },
+ { forceRedirect: 'a' },
+ { forceRedirect: 0 },
+ { forceRedirect: {} },
+ { forceRedirect: [] },
+ { placeholders: true },
+ { placeholders: 'a' },
+ { placeholders: 0 },
+ { placeholders: [] },
+ { pagesPath: true },
+ { pagesPath: 0 },
+ { pagesPath: {} },
+ { pagesPath: [] },
+ { pagesEndpoint: true },
+ { pagesEndpoint: 0 },
+ { pagesEndpoint: {} },
+ { pagesEndpoint: [] },
+ { customUrls: true },
+ { customUrls: 0 },
+ { customUrls: 'a' },
+ { customUrls: [] },
+ { localizationJsonPath: true },
+ { localizationJsonPath: 0 },
+ { localizationJsonPath: {} },
+ { localizationJsonPath: [] },
+ { localizationFallbackLocale: true },
+ { localizationFallbackLocale: 0 },
+ { localizationFallbackLocale: {} },
+ { localizationFallbackLocale: [] },
+ { customRoutes: true },
+ { customRoutes: 0 },
+ { customRoutes: 'a' },
+ { customRoutes: {} },
+ ];
+ for (const option of options) {
+ await expectAsync(reconfigureServerWithPagesConfig(option)).toBeRejected();
+ }
+ });
+ });
+
+ describe('placeholders', () => {
+ it('replaces placeholder in response content', async () => {
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+
+ expect(readFile.calls.all()[0].returnValue).toBeDefined();
+ const originalContent = await readFile.calls.all()[0].returnValue;
+ expect(originalContent).toContain('{{appName}}');
+
+ expect(pageResponse.calls.all()[0].returnValue).toBeDefined();
+ const replacedContent = await pageResponse.calls.all()[0].returnValue;
+ expect(replacedContent.text).not.toContain('{{appName}}');
+ expect(replacedContent.text).toContain(req.config.appName);
+ });
+
+ it('removes undefined placeholder in response content', async () => {
+ await expectAsync(router.goToPage(req, pages.passwordReset)).toBeResolved();
+
+ expect(readFile.calls.all()[0].returnValue).toBeDefined();
+ const originalContent = await readFile.calls.all()[0].returnValue;
+ expect(originalContent).toContain('{{error}}');
+
+ // There is no error placeholder value set by default, so the
+ // {{error}} placeholder should just be removed from content
+ expect(pageResponse.calls.all()[0].returnValue).toBeDefined();
+ const replacedContent = await pageResponse.calls.all()[0].returnValue;
+ expect(replacedContent.text).not.toContain('{{error}}');
+ });
+
+ it('fills placeholders from config object', async () => {
+ config.pages.enableLocalization = false;
+ config.pages.placeholders = {
+ title: 'setViaConfig',
+ };
+ await reconfigureServer(config);
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/custom_json.html',
+ followRedirects: false,
+ method: 'GET',
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain(config.pages.placeholders.title);
+ });
+
+ it('fills placeholders from config function', async () => {
+ config.pages.enableLocalization = false;
+ config.pages.placeholders = () => {
+ return { title: 'setViaConfig' };
+ };
+ await reconfigureServer(config);
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/custom_json.html',
+ followRedirects: false,
+ method: 'GET',
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain(config.pages.placeholders().title);
+ });
+
+ it('fills placeholders from config promise', async () => {
+ config.pages.enableLocalization = false;
+ config.pages.placeholders = async () => {
+ return { title: 'setViaConfig' };
+ };
+ await reconfigureServer(config);
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/custom_json.html',
+ followRedirects: false,
+ method: 'GET',
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain((await config.pages.placeholders()).title);
+ });
+ });
+
+ describe('localization', () => {
+ it('returns default file if localization is disabled', async () => {
+ delete req.config.pages.enableLocalization;
+
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse.calls.all()[0].args[0]).toBeDefined();
+ expect(pageResponse.calls.all()[0].args[0]).not.toMatch(
+ new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+
+ it('returns default file if no locale is specified', async () => {
+ delete req.query.locale;
+
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse.calls.all()[0].args[0]).toBeDefined();
+ expect(pageResponse.calls.all()[0].args[0]).not.toMatch(
+ new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+
+ it('returns custom page regardless of localization enabled', async () => {
+ req.config.pages.customUrls = {
+ passwordResetLinkInvalid: 'http://invalid-link.example.com',
+ };
+
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse).not.toHaveBeenCalled();
+ expect(redirectResponse.calls.all()[0].args[0]).toBe(
+ req.config.pages.customUrls.passwordResetLinkInvalid
+ );
+ });
+
+ it('returns file for locale match', async () => {
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse.calls.all()[0].args[0]).toBeDefined();
+ expect(pageResponse.calls.all()[0].args[0]).toMatch(
+ new RegExp(`\/${req.query.locale}\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+
+ it('returns file for language match', async () => {
+ // Pretend no locale matching file exists
+ spyOn(Utils, 'fileExists').and.callFake(async path => {
+ return !path.includes(
+ `/${req.query.locale}/${pages.passwordResetLinkInvalid.defaultFile}`
+ );
+ });
+
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse.calls.all()[0].args[0]).toBeDefined();
+ expect(pageResponse.calls.all()[0].args[0]).toMatch(
+ new RegExp(`\/de\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+
+ it('returns default file for neither locale nor language match', async () => {
+ req.query.locale = 'yo-LO';
+
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse.calls.all()[0].args[0]).toBeDefined();
+ expect(pageResponse.calls.all()[0].args[0]).not.toMatch(
+ new RegExp(`\/yo(-LO)?\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+ });
+
+ describe('localization with JSON resource', () => {
+ let jsonPageFile;
+ let jsonPageUrl;
+ let jsonResource;
+
+ beforeEach(async () => {
+ jsonPageFile = 'custom_json.html';
+ jsonPageUrl = new URL(`${config.publicServerURL}/apps/${jsonPageFile}`);
+ jsonResource = require('../public/custom_json.json');
+
+ config.pages.enableLocalization = true;
+ config.pages.localizationJsonPath = './public/custom_json.json';
+ config.pages.localizationFallbackLocale = 'en';
+ await reconfigureServer(config);
+ });
+
+ it('does not localize with JSON resource if localization is disabled', async () => {
+ config.pages.enableLocalization = false;
+ config.pages.localizationJsonPath = './public/custom_json.json';
+ config.pages.localizationFallbackLocale = 'en';
+ await reconfigureServer(config);
+
+ const response = await request({
+ url: jsonPageUrl.toString(),
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(pageResponse.calls.all()[0].args[1]).toEqual({});
+ expect(pageResponse.calls.all()[0].args[2]).toEqual({});
+
+ // Ensure header contains no page params
+ const pageParamHeaders = Object.keys(response.headers).filter(header =>
+ header.startsWith(pageParamHeaderPrefix)
+ );
+ expect(pageParamHeaders.length).toBe(0);
+
+ // Ensure page response does not contain any translation
+ const flattenedJson = Utils.flattenObject(jsonResource);
+ for (const value of Object.values(flattenedJson)) {
+ const valueWithoutPlaceholder = fillPlaceholders(value, '');
+ expect(response.text).not.toContain(valueWithoutPlaceholder);
+ }
+ });
+
+ it('localizes static page with JSON resource and fallback locale', async () => {
+ const response = await request({
+ url: jsonPageUrl.toString(),
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+
+ // Ensure page response contains translation of fallback locale
+ const translation = jsonResource[config.pages.localizationFallbackLocale].translation;
+ for (const value of Object.values(translation)) {
+ const valueWithoutPlaceholder = fillPlaceholders(value, '');
+ expect(response.text).toContain(valueWithoutPlaceholder);
+ }
+ });
+
+ it('localizes static page with JSON resource and request locale', async () => {
+ // Add locale to request URL
+ jsonPageUrl.searchParams.set('locale', exampleLocale);
+
+ const response = await request({
+ url: jsonPageUrl.toString(),
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+
+ // Ensure page response contains translations of request locale
+ const translation = jsonResource[exampleLocale].translation;
+ for (const value of Object.values(translation)) {
+ const valueWithoutPlaceholder = fillPlaceholders(value, '');
+ expect(response.text).toContain(valueWithoutPlaceholder);
+ }
+ });
+
+ it('localizes static page with JSON resource and language matching request locale', async () => {
+ // Add locale to request URL that has no locale match but only a language
+ // match in the JSON resource
+ jsonPageUrl.searchParams.set('locale', 'de-CH');
+
+ const response = await request({
+ url: jsonPageUrl.toString(),
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+
+ // Ensure page response contains translations of requst language
+ const translation = jsonResource['de'].translation;
+ for (const value of Object.values(translation)) {
+ const valueWithoutPlaceholder = fillPlaceholders(value, '');
+ expect(response.text).toContain(valueWithoutPlaceholder);
+ }
+ });
+
+ it('localizes static page with JSON resource and fills placeholders in JSON values', async () => {
+ // Add app ID to request URL so that the request is assigned to a Parse Server app
+ // and placeholders within translations strings can be replaced with default page
+ // parameters such as `appId`
+ jsonPageUrl.searchParams.set('appId', config.appId);
+ jsonPageUrl.searchParams.set('locale', exampleLocale);
+
+ const response = await request({
+ url: jsonPageUrl.toString(),
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+
+ // Fill placeholders in transation
+ let translation = jsonResource[exampleLocale].translation;
+ translation = JSON.stringify(translation);
+ translation = mustache.render(translation, { appName: config.appName });
+ translation = JSON.parse(translation);
+
+ // Ensure page response contains translation of request locale
+ for (const value of Object.values(translation)) {
+ expect(response.text).toContain(value);
+ }
+ });
+
+ it('localizes feature page with JSON resource and fills placeholders in JSON values', async () => {
+ // Fake any page to load the JSON page file
+ spyOnProperty(Page.prototype, 'defaultFile').and.returnValue(jsonPageFile);
+
+ const response = await request({
+ url: `http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=${exampleLocale}`,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toEqual(200);
+
+ // Fill placeholders in transation
+ let translation = jsonResource[exampleLocale].translation;
+ translation = JSON.stringify(translation);
+ translation = mustache.render(translation, { appName: config.appName });
+ translation = JSON.parse(translation);
+
+ // Ensure page response contains translation of request locale
+ for (const value of Object.values(translation)) {
+ expect(response.text).toContain(value);
+ }
+ });
+ });
+
+ describe('response type', () => {
+ it('returns a file for GET request', async () => {
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse).toHaveBeenCalled();
+ expect(redirectResponse).not.toHaveBeenCalled();
+ });
+
+ it('returns a redirect for POST request', async () => {
+ req.method = 'POST';
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse).not.toHaveBeenCalled();
+ expect(redirectResponse).toHaveBeenCalled();
+ });
+
+ it('returns a redirect for custom pages for GET and POST request', async () => {
+ req.config.pages.customUrls = {
+ passwordResetLinkInvalid: 'http://invalid-link.example.com',
+ };
+
+ for (const method of ['GET', 'POST']) {
+ req.method = method;
+ await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved();
+ expect(pageResponse).not.toHaveBeenCalled();
+ expect(redirectResponse).toHaveBeenCalled();
+ }
+ });
+
+ it('responds to POST request with redirect response', async () => {
+ await reconfigureServer(config);
+ const response = await request({
+ url:
+ 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT',
+ followRedirects: false,
+ method: 'POST',
+ });
+ expect(response.status).toEqual(303);
+ expect(response.headers.location).toContain(
+ 'http://localhost:8378/1/apps/de-AT/password_reset_link_invalid.html'
+ );
+ });
+
+ it('responds to GET request with content response', async () => {
+ await reconfigureServer(config);
+ const response = await request({
+ url:
+ 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT',
+ followRedirects: false,
+ method: 'GET',
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('');
+ });
+ });
+
+ describe('end-to-end tests', () => {
+ it('localizes end-to-end for password reset: success', async () => {
+ await reconfigureServer(config);
+ const sendPasswordResetEmail = spyOn(
+ config.emailAdapter,
+ 'sendPasswordResetEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await Parse.User.requestPasswordReset(user.getEmail());
+
+ const link = sendPasswordResetEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const appId = linkResponse.headers['x-parse-page-param-appid'];
+ const token = linkResponse.headers['x-parse-page-param-token'];
+ const locale = linkResponse.headers['x-parse-page-param-locale'];
+ const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
+ const passwordResetPagePath = pageResponse.calls.all()[0].args[0];
+ expect(appId).toBeDefined();
+ expect(token).toBeDefined();
+ expect(locale).toBeDefined();
+ expect(publicServerUrl).toBeDefined();
+ expect(passwordResetPagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.passwordReset.defaultFile}`)
+ );
+ pageResponse.calls.reset();
+
+ const formUrl = `${publicServerUrl}/apps/${appId}/request_password_reset`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ token,
+ locale,
+ new_password: 'newPassword',
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(200);
+ expect(pageResponse.calls.all()[0].args[0]).toContain(
+ `/${locale}/${pages.passwordResetSuccess.defaultFile}`
+ );
+ });
+
+ it('localizes end-to-end for password reset: invalid link', async () => {
+ await reconfigureServer(config);
+ const sendPasswordResetEmail = spyOn(
+ config.emailAdapter,
+ 'sendPasswordResetEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await Parse.User.requestPasswordReset(user.getEmail());
+
+ const link = sendPasswordResetEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+ linkWithLocale.searchParams.set(pageParams.token, 'invalidToken');
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const pagePath = pageResponse.calls.all()[0].args[0];
+ expect(pagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.passwordResetLinkInvalid.defaultFile}`)
+ );
+ });
+
+ it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)('localizes end-to-end for verify email: success', async () => {
+ await reconfigureServer(config);
+ const sendVerificationEmail = spyOn(
+ config.emailAdapter,
+ 'sendVerificationEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await jasmine.timeout();
+
+ const link = sendVerificationEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const pagePath = pageResponse.calls.all()[0].args[0];
+ expect(pagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}`)
+ );
+ });
+
+ it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)('localizes end-to-end for verify email: invalid verification link - link send success', async () => {
+ await reconfigureServer(config);
+ const sendVerificationEmail = spyOn(
+ config.emailAdapter,
+ 'sendVerificationEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await jasmine.timeout();
+
+ const link = sendVerificationEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+ linkWithLocale.searchParams.set(pageParams.token, 'invalidToken');
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const appId = linkResponse.headers['x-parse-page-param-appid'];
+ const locale = linkResponse.headers['x-parse-page-param-locale'];
+ const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
+ const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0];
+ expect(appId).toBeDefined();
+ expect(locale).toBe(exampleLocale);
+ expect(publicServerUrl).toBeDefined();
+ expect(invalidVerificationPagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`)
+ );
+
+ const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ locale,
+ username: 'exampleUsername',
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(303);
+ expect(formResponse.text).toContain(
+ `/${locale}/${pages.emailVerificationSendSuccess.defaultFile}`
+ );
+ });
+
+ it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)('localizes end-to-end for verify email: invalid verification link - link send fail', async () => {
+ await reconfigureServer(config);
+ const sendVerificationEmail = spyOn(
+ config.emailAdapter,
+ 'sendVerificationEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await jasmine.timeout();
+
+ const link = sendVerificationEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+ linkWithLocale.searchParams.set(pageParams.token, 'invalidToken');
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const appId = linkResponse.headers['x-parse-page-param-appid'];
+ const locale = linkResponse.headers['x-parse-page-param-locale'];
+ const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
+ await jasmine.timeout();
+
+ const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0];
+ expect(appId).toBeDefined();
+ expect(locale).toBe(exampleLocale);
+ expect(publicServerUrl).toBeDefined();
+ expect(invalidVerificationPagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`)
+ );
+
+ spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() =>
+ Promise.reject('failed to resend verification email')
+ );
+
+ const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ locale,
+ username: 'exampleUsername',
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(303);
+ // With emailVerifySuccessOnInvalidEmail: true (default), the resend
+ // page always redirects to the success page to prevent user enumeration
+ expect(formResponse.text).toContain(
+ `/${locale}/${pages.emailVerificationSendSuccess.defaultFile}`
+ );
+ });
+
+ it('localizes end-to-end for verify email: invalid verification link - link send fail with emailVerifySuccessOnInvalidEmail disabled', async () => {
+ config.emailVerifySuccessOnInvalidEmail = false;
+ await reconfigureServer(config);
+ const sendVerificationEmail = spyOn(
+ config.emailAdapter,
+ 'sendVerificationEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await jasmine.timeout();
+
+ const link = sendVerificationEmail.calls.all()[0].args[0].link;
+ const linkWithLocale = new URL(link);
+ linkWithLocale.searchParams.append(pageParams.locale, exampleLocale);
+ linkWithLocale.searchParams.set(pageParams.token, 'invalidToken');
+
+ const linkResponse = await request({
+ url: linkWithLocale.toString(),
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const appId = linkResponse.headers['x-parse-page-param-appid'];
+ const locale = linkResponse.headers['x-parse-page-param-locale'];
+ const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
+ await jasmine.timeout();
+
+ const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0];
+ expect(appId).toBeDefined();
+ expect(locale).toBe(exampleLocale);
+ expect(publicServerUrl).toBeDefined();
+ expect(invalidVerificationPagePath).toMatch(
+ new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`)
+ );
+
+ spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() =>
+ Promise.reject('failed to resend verification email')
+ );
+
+ const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ locale,
+ username: 'exampleUsername',
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(303);
+ // With emailVerifySuccessOnInvalidEmail: false, the resend page
+ // redirects to the fail page
+ expect(formResponse.text).toContain(
+ `/${locale}/${pages.emailVerificationSendFail.defaultFile}`
+ );
+ });
+
+ it('localizes end-to-end for resend verification email: invalid link', async () => {
+ await reconfigureServer(config);
+ const formUrl = `${config.publicServerURL}/apps/${config.appId}/resend_verification_email`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ locale: exampleLocale,
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(303);
+ expect(formResponse.text).toContain(
+ `/${exampleLocale}/${pages.emailVerificationLinkInvalid.defaultFile}`
+ );
+ });
+ });
+
+ describe('failing with missing parameters', () => {
+ it('verifyEmail: throws on missing server configuration', async () => {
+ delete req.config;
+ const verifyEmail = req => (() => new PagesRouter().verifyEmail(req)).bind(null);
+ expect(verifyEmail(req)).toThrow();
+ });
+
+ it('resendVerificationEmail: throws on missing server configuration', async () => {
+ delete req.config;
+ const resendVerificationEmail = req =>
+ (() => new PagesRouter().resendVerificationEmail(req)).bind(null);
+ expect(resendVerificationEmail(req)).toThrow();
+ });
+
+ it('requestResetPassword: throws on missing server configuration', async () => {
+ delete req.config;
+ const requestResetPassword = req =>
+ (() => new PagesRouter().requestResetPassword(req)).bind(null);
+ expect(requestResetPassword(req)).toThrow();
+ });
+
+ it('resetPassword: throws on missing server configuration', async () => {
+ delete req.config;
+ const resetPassword = req => (() => new PagesRouter().resetPassword(req)).bind(null);
+ expect(resetPassword(req)).toThrow();
+ });
+
+ it('verifyEmail: responds with invalid link on missing username', async () => {
+ req.query.token = 'exampleToken';
+ req.params = {};
+ req.config.userController = { verifyEmail: () => Promise.reject() };
+ const verifyEmail = req => new PagesRouter().verifyEmail(req);
+
+ await verifyEmail(req);
+ expect(goToPage.calls.all()[0].args[1]).toBe(pages.emailVerificationLinkInvalid);
+ });
+
+ it('resetPassword: responds with page choose password with error message on failed password update', async () => {
+ req.body = {
+ token: 'exampleToken',
+ username: 'exampleUsername',
+ new_password: 'examplePassword',
+ };
+ const error = 'exampleError';
+ req.config.userController = { updatePassword: () => Promise.reject(error) };
+ const resetPassword = req => new PagesRouter().resetPassword(req);
+
+ await resetPassword(req);
+ expect(goToPage.calls.all()[0].args[1]).toBe(pages.passwordReset);
+ expect(goToPage.calls.all()[0].args[2].error).toBe(error);
+ });
+
+ it('resetPassword: responds with AJAX error with error message on failed password update', async () => {
+ req.xhr = true;
+ req.body = {
+ token: 'exampleToken',
+ username: 'exampleUsername',
+ new_password: 'examplePassword',
+ };
+ const error = 'exampleError';
+ req.config.userController = { updatePassword: () => Promise.reject(error) };
+ const resetPassword = req => new PagesRouter().resetPassword(req).catch(e => e);
+
+ const response = await resetPassword(req);
+ expect(response.code).toBe(Parse.Error.OTHER_CAUSE);
+ });
+ });
+
+ describe('exploits', () => {
+ it('rejects requesting file outside of pages scope with UNIX path patterns', async () => {
+ await reconfigureServer(config);
+
+ // Do not compose this URL with `new URL(...)` because that would normalize
+ // the URL and remove path patterns; the path patterns must reach the router
+ const url = `${config.publicServerURL}/apps/../.gitignore`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(404);
+ expect(response.text).toBe('Not found.');
+ });
+
+ it('rejects requesting file from sibling directory with prefix-colliding name via encoded path traversal', async () => {
+ // Create a temporary pages directory and a sibling directory whose name
+ // starts with the same prefix (e.g. "pages" vs "pages-secret"), which
+ // would bypass a naive `startsWith` check without a path separator.
+ const baseDir = path.join(__dirname, 'tmp-pages-exploit-test');
+ const pagesDir = path.join(baseDir, 'pages');
+ const siblingDir = path.join(baseDir, 'pages-secret');
+ const marker = `SECRET_CONTENT_${Date.now()}`;
+
+ try {
+ await fs.mkdir(pagesDir, { recursive: true });
+ await fs.mkdir(siblingDir, { recursive: true });
+ // Copy a required HTML file so the pages router initializes correctly
+ const publicDir = path.resolve(__dirname, '../public');
+ const htmlFile = await fs.readFile(
+ path.join(publicDir, 'email_verification_link_invalid.html'),
+ 'utf-8'
+ );
+ await fs.writeFile(
+ path.join(pagesDir, 'email_verification_link_invalid.html'),
+ htmlFile
+ );
+ // Write a secret file in the sibling directory
+ await fs.writeFile(path.join(siblingDir, 'secret.txt'), marker);
+
+ config.pages.pagesPath = pagesDir;
+ await reconfigureServer(config);
+
+ // Use URL-encoded path traversal: %2e%2e%2f = ../
+ // This reaches the sibling "pages-secret" directory which shares
+ // the "pages" prefix with the configured pagesPath directory name.
+ const url = `${config.publicServerURL}/apps/%2e%2e%2fpages-secret%2fsecret.txt`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+
+ expect(response.status).toBe(404);
+ expect(response.text).not.toContain(marker);
+ } finally {
+ await fs.rm(baseDir, { recursive: true, force: true });
+ }
+ });
+
+ it('rejects non-string token in verifyEmail', async () => {
+ await reconfigureServer(config);
+ const url = `${config.publicServerURL}/apps/test/verify_email?token[toString]=abc`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).not.toBe(500);
+ });
+
+ it('rejects non-string token in requestResetPassword', async () => {
+ await reconfigureServer(config);
+ const url = `${config.publicServerURL}/apps/test/request_password_reset?token[toString]=abc`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).not.toBe(500);
+ });
+
+ it('rejects non-string token in resetPassword via POST', async () => {
+ await reconfigureServer(config);
+ const url = `${config.publicServerURL}/apps/test/request_password_reset`;
+ const response = await request({
+ method: 'POST',
+ url: url,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: { token: { toString: 'abc' }, new_password: 'newpass123' },
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).not.toBe(500);
+ });
+
+ it('rejects non-string token in resendVerificationEmail via POST', async () => {
+ await reconfigureServer(config);
+ const url = `${config.publicServerURL}/apps/test/resend_verification_email`;
+ const response = await request({
+ method: 'POST',
+ url: url,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: { token: { toString: 'abc' } },
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).not.toBe(500);
+ });
+
+ it('does not leak email verification status via resend page when emailVerifySuccessOnInvalidEmail is true', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => {},
+ sendPasswordResetEmail: () => {},
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ ...config,
+ verifyUserEmails: true,
+ emailVerifySuccessOnInvalidEmail: true,
+ emailAdapter,
+ });
+
+ // Create a user with unverified email
+ const user = new Parse.User();
+ user.setUsername('realuser');
+ user.setPassword('password123');
+ user.setEmail('real@example.com');
+ await user.signUp();
+
+ const formUrl = `${config.publicServerURL}/apps/${config.appId}/resend_verification_email`;
+
+ // Resend for existing unverified user
+ const existingResponse = await request({
+ method: 'POST',
+ url: formUrl,
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: 'username=realuser',
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Resend for non-existing user
+ const nonExistingResponse = await request({
+ method: 'POST',
+ url: formUrl,
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: 'username=fakeuser',
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Both should redirect to the same page (success) to prevent enumeration
+ expect(existingResponse.status).toBe(303);
+ expect(nonExistingResponse.status).toBe(303);
+ expect(existingResponse.headers.location).toContain('email_verification_send_success');
+ expect(nonExistingResponse.headers.location).toContain('email_verification_send_success');
+ });
+
+ it('does leak email verification status via resend page when emailVerifySuccessOnInvalidEmail is false', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => {},
+ sendPasswordResetEmail: () => {},
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ ...config,
+ verifyUserEmails: true,
+ emailVerifySuccessOnInvalidEmail: false,
+ emailAdapter,
+ });
+
+ const formUrl = `${config.publicServerURL}/apps/${config.appId}/resend_verification_email`;
+
+ // Resend for non-existing user should redirect to fail page
+ const nonExistingResponse = await request({
+ method: 'POST',
+ url: formUrl,
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ body: 'username=fakeuser',
+ followRedirects: false,
+ }).catch(e => e);
+
+ expect(nonExistingResponse.status).toBe(303);
+ expect(nonExistingResponse.headers.location).toContain('email_verification_send_fail');
+ });
+
+ it('does not create file existence oracle via path traversal in locale query parameter', async () => {
+ // Create a canary file at a traversable path to test the oracle
+ const canaryDir = path.join(__dirname, 'tmp-locale-oracle-test');
+ try {
+ await fs.mkdir(canaryDir, { recursive: true });
+ await fs.writeFile(path.join(canaryDir, 'password_reset.html'), 'canary');
+
+ config.pages.enableLocalization = true;
+ await reconfigureServer(config);
+
+ // Calculate traversal from pages directory to canary directory
+ const pagesPath = path.resolve(__dirname, '../public');
+ const relativePath = path.relative(pagesPath, canaryDir);
+
+ // Request with path traversal locale pointing to existing canary file
+ const existsResponse = await request({
+ url: `${config.publicServerURL}/apps/${config.appId}/request_password_reset?token=test&username=test&locale=${encodeURIComponent(relativePath)}`,
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Request with path traversal locale pointing to non-existing directory
+ const notExistsResponse = await request({
+ url: `${config.publicServerURL}/apps/${config.appId}/request_password_reset?token=test&username=test&locale=${encodeURIComponent('../../../../../../tmp/nonexistent-dir')}`,
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Both responses must have the same status â no differential oracle
+ expect(existsResponse.status).toBe(notExistsResponse.status);
+ // Canary content must never be served
+ expect(existsResponse.text).not.toContain('canary');
+ expect(notExistsResponse.text).not.toContain('canary');
+ } finally {
+ await fs.rm(canaryDir, { recursive: true, force: true });
+ }
+ });
+
+ it('does not create file existence oracle via path traversal in locale header', async () => {
+ // Create a canary file at a traversable path
+ const canaryDir = path.join(__dirname, 'tmp-locale-header-test');
+ try {
+ await fs.mkdir(canaryDir, { recursive: true });
+ await fs.writeFile(path.join(canaryDir, 'password_reset.html'), 'canary');
+
+ config.pages.enableLocalization = true;
+ await reconfigureServer(config);
+
+ const pagesPath = path.resolve(__dirname, '../public');
+ const relativePath = path.relative(pagesPath, canaryDir);
+
+ // Request with path traversal locale via header pointing to existing file
+ const existsResponse = await request({
+ url: `${config.publicServerURL}/apps/${config.appId}/request_password_reset?token=test&username=test`,
+ headers: { 'x-parse-page-param-locale': relativePath },
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Request with path traversal locale via header pointing to non-existing directory
+ const notExistsResponse = await request({
+ url: `${config.publicServerURL}/apps/${config.appId}/request_password_reset?token=test&username=test`,
+ headers: { 'x-parse-page-param-locale': '../../../../../../tmp/nonexistent-dir' },
+ followRedirects: false,
+ }).catch(e => e);
+
+ // Both responses must have the same status â no differential oracle
+ expect(existsResponse.status).toBe(notExistsResponse.status);
+ // Canary content must never be served
+ expect(existsResponse.text).not.toContain('canary');
+ expect(notExistsResponse.text).not.toContain('canary');
+ } finally {
+ await fs.rm(canaryDir, { recursive: true, force: true });
+ }
+ });
+ });
+
+ describe('custom route', () => {
+ it('handles custom route with GET', async () => {
+ config.pages.customRoutes = [
+ {
+ method: 'GET',
+ path: 'custom_page',
+ handler: async req => {
+ expect(req).toBeDefined();
+ expect(req.method).toBe('GET');
+ return { file: 'custom_page.html' };
+ },
+ },
+ ];
+ await reconfigureServer(config);
+ const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough();
+
+ const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toMatch(config.appName);
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('handles custom route with POST', async () => {
+ config.pages.customRoutes = [
+ {
+ method: 'POST',
+ path: 'custom_page',
+ handler: async req => {
+ expect(req).toBeDefined();
+ expect(req.method).toBe('POST');
+ return { file: 'custom_page.html' };
+ },
+ },
+ ];
+ const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough();
+ await reconfigureServer(config);
+
+ const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ method: 'POST',
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toMatch(config.appName);
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('handles multiple custom routes', async () => {
+ config.pages.customRoutes = [
+ {
+ method: 'GET',
+ path: 'custom_page',
+ handler: async req => {
+ expect(req).toBeDefined();
+ expect(req.method).toBe('GET');
+ return { file: 'custom_page.html' };
+ },
+ },
+ {
+ method: 'POST',
+ path: 'custom_page',
+ handler: async req => {
+ expect(req).toBeDefined();
+ expect(req.method).toBe('POST');
+ return { file: 'custom_page.html' };
+ },
+ },
+ ];
+ const getHandlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough();
+ const postHandlerSpy = spyOn(config.pages.customRoutes[1], 'handler').and.callThrough();
+ await reconfigureServer(config);
+
+ const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`;
+ const getResponse = await request({
+ url: url,
+ followRedirects: false,
+ method: 'GET',
+ }).catch(e => e);
+ expect(getResponse.status).toBe(200);
+ expect(getResponse.text).toMatch(config.appName);
+ expect(getHandlerSpy).toHaveBeenCalled();
+
+ const postResponse = await request({
+ url: url,
+ followRedirects: false,
+ method: 'POST',
+ }).catch(e => e);
+ expect(postResponse.status).toBe(200);
+ expect(postResponse.text).toMatch(config.appName);
+ expect(postHandlerSpy).toHaveBeenCalled();
+ });
+
+ it('handles custom route with async handler', async () => {
+ config.pages.customRoutes = [
+ {
+ method: 'GET',
+ path: 'custom_page',
+ handler: async req => {
+ expect(req).toBeDefined();
+ expect(req.method).toBe('GET');
+ const file = await new Promise(resolve =>
+ setTimeout(resolve('custom_page.html'), 1000)
+ );
+ return { file };
+ },
+ },
+ ];
+ await reconfigureServer(config);
+ const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough();
+
+ const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toMatch(config.appName);
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+
+ it('returns 404 if custom route does not return page', async () => {
+ config.pages.customRoutes = [
+ {
+ method: 'GET',
+ path: 'custom_page',
+ handler: async () => {},
+ },
+ ];
+ await reconfigureServer(config);
+ const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough();
+
+ const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`;
+ const response = await request({
+ url: url,
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(404);
+ expect(response.text).toMatch('Not found');
+ expect(handlerSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('custom endpoint', () => {
+ it('password reset works with custom endpoint', async () => {
+ config.pages.pagesEndpoint = 'customEndpoint';
+ await reconfigureServer(config);
+ const sendPasswordResetEmail = spyOn(
+ config.emailAdapter,
+ 'sendPasswordResetEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await Parse.User.requestPasswordReset(user.getEmail());
+
+ const link = sendPasswordResetEmail.calls.all()[0].args[0].link;
+ const linkResponse = await request({
+ url: link,
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+
+ const appId = linkResponse.headers['x-parse-page-param-appid'];
+ const token = linkResponse.headers['x-parse-page-param-token'];
+ const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
+ const passwordResetPagePath = pageResponse.calls.all()[0].args[0];
+ expect(appId).toBeDefined();
+ expect(token).toBeDefined();
+ expect(publicServerUrl).toBeDefined();
+ expect(passwordResetPagePath).toMatch(new RegExp(`\/${pages.passwordReset.defaultFile}`));
+ pageResponse.calls.reset();
+
+ const formUrl = `${publicServerUrl}/${config.pages.pagesEndpoint}/${appId}/request_password_reset`;
+ const formResponse = await request({
+ url: formUrl,
+ method: 'POST',
+ body: {
+ token,
+ new_password: 'newPassword',
+ },
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+ followRedirects: false,
+ });
+ expect(formResponse.status).toEqual(200);
+ expect(pageResponse.calls.all()[0].args[0]).toContain(
+ `/${pages.passwordResetSuccess.defaultFile}`
+ );
+ });
+
+ it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)('email verification works with custom endpoint', async () => {
+ config.pages.pagesEndpoint = 'customEndpoint';
+ await reconfigureServer(config);
+ const sendVerificationEmail = spyOn(
+ config.emailAdapter,
+ 'sendVerificationEmail'
+ ).and.callThrough();
+ const user = new Parse.User();
+ user.setUsername('exampleUsername');
+ user.setPassword('examplePassword');
+ user.set('email', 'mail@example.com');
+ await user.signUp();
+ await jasmine.timeout();
+
+ const link = sendVerificationEmail.calls.all()[0].args[0].link;
+ const linkResponse = await request({
+ url: link,
+ followRedirects: false,
+ });
+ expect(linkResponse.status).toBe(200);
+ const pagePath = pageResponse.calls.all()[0].args[0];
+ expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`));
+ });
+ });
+ });
+
+ describe('async publicServerURL', () => {
+ it('resolves async publicServerURL for password reset page', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'exampleAppname',
+ verifyUserEmails: true,
+ emailAdapter,
+ publicServerURL: () => 'http://localhost:8378/1',
+ });
+
+ const user = new Parse.User();
+ user.setUsername('asyncUrlUser');
+ user.setPassword('examplePassword');
+ user.set('email', 'async-url@example.com');
+ await user.signUp();
+ await Parse.User.requestPasswordReset('async-url@example.com');
+
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/test/request_password_reset?token=invalidToken',
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toContain('Invalid password reset link!');
+ });
+
+ it('resolves async publicServerURL for email verification page', async () => {
+ const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+ };
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'exampleAppname',
+ verifyUserEmails: true,
+ emailAdapter,
+ publicServerURL: () => 'http://localhost:8378/1',
+ });
+
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/test/verify_email?token=invalidToken',
+ followRedirects: false,
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toContain('Invalid verification link!');
+ });
+ });
+
+ describe('pagesPath resolution', () => {
+ it('should serve pages when current working directory differs from module directory', async () => {
+ const originalCwd = process.cwd();
+ const os = require('os');
+ process.chdir(os.tmpdir());
+
+ try {
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'exampleAppname',
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ // Request the password reset page with an invalid token;
+ // even with an invalid token, the server should serve the
+ // "invalid link" page (200), not a 404. A 404 indicates the
+ // HTML template files could not be found because pagesPath
+ // resolved to the wrong directory.
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/test/request_password_reset?token=invalidToken',
+ }).catch(e => e);
+ expect(response.status).toBe(200);
+ expect(response.text).toContain('Invalid password reset link');
+ } finally {
+ process.chdir(originalCwd);
+ }
+ });
+ });
+
+ describe('special characters in config', () => {
+ it('should not URI-encode page param headers by default', async () => {
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'ExampleAppName',
+ publicServerURL: 'http://localhost:8378/1',
+ });
+
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/choose_password?appId=test',
+ });
+ expect(response.status).toBe(200);
+ expect(response.headers['x-parse-page-param-appname']).toBe('ExampleAppName');
+ expect(response.headers['x-parse-page-param-publicserverurl']).toBe(
+ 'http://localhost:8378/1'
+ );
+ });
+
+ it('should URI-encode page param headers when encodePageParamHeaders is true', async () => {
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'Productâĸ',
+ publicServerURL: 'http://localhost:8378/1',
+ pages: {
+ encodePageParamHeaders: true,
+ },
+ });
+
+ const response = await request({
+ url: 'http://localhost:8378/1/apps/choose_password?appId=test',
+ });
+ expect(response.status).toBe(200);
+ expect(response.headers['x-parse-page-param-appname']).toBe(
+ encodeURIComponent('Productâĸ')
+ );
+ expect(response.headers['x-parse-page-param-publicserverurl']).toBe(
+ encodeURIComponent('http://localhost:8378/1')
+ );
+ });
+ });
+
+ describe('XSS Protection', () => {
+ beforeEach(async () => {
+ await reconfigureServer({
+ appId: 'test',
+ appName: 'exampleAppname',
+ publicServerURL: 'http://localhost:8378/1',
+ });
+ });
+
+ it('should escape XSS payloads in token parameter', async () => {
+ const xssPayload = '">';
+ const response = await request({
+ url: `http://localhost:8378/1/apps/choose_password?token=${encodeURIComponent(xssPayload)}&username=test&appId=test`,
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.text).not.toContain('');
+ expect(response.text).toContain('"><script>');
+ });
+
+ it('should escape XSS in username parameter', async () => {
+ const xssUsername = '
';
+ const response = await request({
+ url: `http://localhost:8378/1/apps/choose_password?username=${encodeURIComponent(xssUsername)}&appId=test`,
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.text).not.toContain('
');
+ expect(response.text).toContain('<img');
+ });
+
+ it('should reject XSS payload in locale parameter', async () => {
+ const xssLocale = '">