} 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/classes.js b/classes.js
deleted file mode 100644
index 98e948714d..0000000000
--- a/classes.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// These methods handle the 'classes' routes.
-// Methods of the form 'handleX' return promises and are intended to
-// be used with the PromiseRouter.
-
-var Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- rest = require('./rest');
-
-var router = new PromiseRouter();
-
-// Returns a promise that resolves to a {response} object.
-function handleFind(req) {
- var body = Object.assign(req.body, req.query);
- var options = {};
- if (body.skip) {
- options.skip = Number(body.skip);
- }
- if (body.limit) {
- options.limit = Number(body.limit);
- }
- if (body.order) {
- options.order = String(body.order);
- }
- if (body.count) {
- options.count = true;
- }
- if (typeof body.keys == 'string') {
- options.keys = body.keys;
- }
- if (body.include) {
- options.include = String(body.include);
- }
- if (body.redirectClassNameForKey) {
- options.redirectClassNameForKey = String(body.redirectClassNameForKey);
- }
-
- if(typeof body.where === 'string') {
- body.where = JSON.parse(body.where);
- }
-
- return rest.find(req.config, req.auth,
- req.params.className, body.where, options)
- .then((response) => {
- if (response && response.results) {
- for (result of response.results) {
- if (result.sessionToken) {
- result.sessionToken = req.info.sessionToken || result.sessionToken;
- }
- }
- response.results.sessionToken
- }
- return {response: response};
- });
-}
-
-// Returns a promise for a {status, response, location} object.
-function handleCreate(req) {
- return rest.create(req.config, req.auth,
- req.params.className, req.body);
-}
-
-// Returns a promise for a {response} object.
-function handleGet(req) {
- return rest.find(req.config, req.auth,
- req.params.className, {objectId: req.params.objectId})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
- 'Object not found.');
- } else {
- return {response: response.results[0]};
- }
- });
-}
-
-// Returns a promise for a {response} object.
-function handleDelete(req) {
- return rest.del(req.config, req.auth,
- req.params.className, req.params.objectId)
- .then(() => {
- return {response: {}};
- });
-}
-
-// Returns a promise for a {response} object.
-function handleUpdate(req) {
- return rest.update(req.config, req.auth,
- req.params.className, req.params.objectId, req.body)
- .then((response) => {
- return {response: response};
- });
-}
-
-router.route('GET', '/classes/:className', handleFind);
-router.route('POST', '/classes/:className', handleCreate);
-router.route('GET', '/classes/:className/:objectId', handleGet);
-router.route('DELETE', '/classes/:className/:objectId', handleDelete);
-router.route('PUT', '/classes/:className/:objectId', handleUpdate);
-
-module.exports = router;
-
diff --git a/cloud/main.js b/cloud/main.js
deleted file mode 100644
index fec259910a..0000000000
--- a/cloud/main.js
+++ /dev/null
@@ -1,104 +0,0 @@
-var Parse = require('parse/node').Parse;
-
-Parse.Cloud.define('hello', function(req, res) {
- res.success('Hello world!');
-});
-
-Parse.Cloud.beforeSave('BeforeSaveFail', function(req, res) {
- res.error('You shall not pass!');
-});
-
-Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function (req, res) {
- var query = new Parse.Query('Yolo');
- query.find().then(() => {
- res.error('Nope');
- }, () => {
- res.success();
- });
-});
-
-Parse.Cloud.beforeSave('BeforeSaveUnchanged', function(req, res) {
- res.success();
-});
-
-Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
- req.object.set('foo', 'baz');
- res.success();
-});
-
-Parse.Cloud.afterSave('AfterSaveTest', function(req) {
- var obj = new Parse.Object('AfterSaveProof');
- obj.set('proof', req.object.id);
- obj.save();
-});
-
-Parse.Cloud.beforeDelete('BeforeDeleteFail', function(req, res) {
- res.error('Nope');
-});
-
-Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function (req, res) {
- var query = new Parse.Query('Yolo');
- query.find().then(() => {
- res.error('Nope');
- }, () => {
- res.success();
- });
-});
-
-Parse.Cloud.beforeDelete('BeforeDeleteTest', function(req, res) {
- res.success();
-});
-
-Parse.Cloud.afterDelete('AfterDeleteTest', function(req) {
- var obj = new Parse.Object('AfterDeleteProof');
- obj.set('proof', req.object.id);
- obj.save();
-});
-
-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.');
- }
-});
-
-Parse.Cloud.afterSave('SaveTriggerUser', function(req) {
- if (!req.user || !req.user.id) {
- console.log('No user present on request object for afterSave.');
- }
-});
-
-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
- });
-});
-
-Parse.Cloud.define('bar', function(req, res) {
- res.error('baz');
-});
-
-Parse.Cloud.define('requiredParameterCheck', function(req, res) {
- res.success();
-}, function(params) {
- return params.name;
-});
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/facebook.js b/facebook.js
deleted file mode 100644
index 5f9bbee85e..0000000000
--- a/facebook.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Helper functions for accessing the Facebook Graph API.
-var https = require('https');
-var Parse = require('parse/node').Parse;
-
-// Returns a promise that fulfills iff this user id is valid.
-function validateUserId(userId, access_token) {
- return graphRequest('me?fields=id&access_token=' + access_token)
- .then((data) => {
- if (data && data.id == userId) {
- return;
- }
- throw new Parse.Error(
- Parse.Error.OBJECT_NOT_FOUND,
- 'Facebook auth is invalid for this user.');
- });
-}
-
-// Returns a promise that fulfills iff this app id is valid.
-function validateAppId(appIds, access_token) {
- if (!appIds.length) {
- throw new Parse.Error(
- Parse.Error.OBJECT_NOT_FOUND,
- 'Facebook auth is not configured.');
- }
- return graphRequest('app?access_token=' + access_token)
- .then((data) => {
- if (data && appIds.indexOf(data.id) != -1) {
- return;
- }
- throw new Parse.Error(
- Parse.Error.OBJECT_NOT_FOUND,
- 'Facebook auth is invalid for this user.');
- });
-}
-
-// A promisey wrapper for FB graph requests.
-function graphRequest(path) {
- return new Promise(function(resolve, reject) {
- https.get('https://graph.facebook.com/v2.5/' + path, function(res) {
- var data = '';
- res.on('data', function(chunk) {
- data += chunk;
- });
- res.on('end', function() {
- data = JSON.parse(data);
- resolve(data);
- });
- }).on('error', function(e) {
- reject('Failed to validate this access token with Facebook.');
- });
- });
-}
-
-module.exports = {
- validateAppId: validateAppId,
- validateUserId: validateUserId
-};
diff --git a/files.js b/files.js
deleted file mode 100644
index a840e098de..0000000000
--- a/files.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// files.js
-
-var bodyParser = require('body-parser'),
- Config = require('./Config'),
- express = require('express'),
- FilesAdapter = require('./FilesAdapter'),
- middlewares = require('./middlewares.js'),
- mime = require('mime'),
- Parse = require('parse/node').Parse,
- rack = require('hat').rack();
-
-var router = express.Router();
-
-var processCreate = function(req, res, next) {
- if (!req.body || !req.body.length) {
- next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR,
- 'Invalid file upload.'));
- return;
- }
-
- if (req.params.filename.length > 128) {
- next(new Parse.Error(Parse.Error.INVALID_FILE_NAME,
- 'Filename too long.'));
- return;
- }
-
- if (!req.params.filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
- next(new Parse.Error(Parse.Error.INVALID_FILE_NAME,
- 'Filename contains invalid characters.'));
- return;
- }
-
- // If a content-type is included, we'll add an extension so we can
- // return the same content-type.
- var extension = '';
- var hasExtension = req.params.filename.indexOf('.') > 0;
- var contentType = req.get('Content-type');
- if (!hasExtension && contentType && mime.extension(contentType)) {
- extension = '.' + mime.extension(contentType);
- }
-
- var filename = rack() + '_' + req.params.filename + extension;
- FilesAdapter.getAdapter().create(req.config, filename, req.body)
- .then(() => {
- res.status(201);
- var location = FilesAdapter.getAdapter().location(req.config, req, filename);
- res.set('Location', location);
- res.json({ url: location, name: filename });
- }).catch((error) => {
- next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR,
- 'Could not store file.'));
- });
-};
-
-var processGet = function(req, res) {
- var config = new Config(req.params.appId);
- FilesAdapter.getAdapter().get(config, req.params.filename)
- .then((data) => {
- res.status(200);
- var contentType = mime.lookup(req.params.filename);
- res.set('Content-type', contentType);
- res.end(data);
- }).catch((error) => {
- res.status(404);
- res.set('Content-type', 'text/plain');
- res.end('File not found.');
- });
-};
-
-router.get('/files/:appId/:filename', processGet);
-
-router.post('/files', function(req, res, next) {
- next(new Parse.Error(Parse.Error.INVALID_FILE_NAME,
- 'Filename not provided.'));
-});
-
-router.post('/files/:filename',
- middlewares.allowCrossDomain,
- bodyParser.raw({type: '*/*', limit: '20mb'}),
- middlewares.handleParseHeaders,
- processCreate);
-
-module.exports = {
- router: router
-};
diff --git a/functions.js b/functions.js
deleted file mode 100644
index 09e43ed344..0000000000
--- a/functions.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// functions.js
-
-var express = require('express'),
- Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- rest = require('./rest');
-
-var router = new PromiseRouter();
-
-function handleCloudFunction(req) {
- if (Parse.Cloud.Functions[req.params.functionName]) {
- if (Parse.Cloud.Validators[req.params.functionName]) {
- var result = Parse.Cloud.Validators[req.params.functionName](req.body || {});
- if (!result) {
- throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Validation failed.');
- }
- }
-
- return new Promise(function (resolve, reject) {
- var response = createResponseObject(resolve, reject);
- var request = {
- params: req.body || {},
- master: req.auth && req.auth.isMaster,
- user: req.auth && req.auth.user,
- };
- Parse.Cloud.Functions[req.params.functionName](request, response);
- });
- } else {
- throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid function.');
- }
-}
-
-function createResponseObject(resolve, reject) {
- return {
- success: function(result) {
- resolve({
- response: {
- result: Parse._encode(result)
- }
- });
- },
- error: function(error) {
- reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error));
- }
- }
-}
-
-router.route('POST', '/functions/:functionName', handleCloudFunction);
-
-
-module.exports = router;
diff --git a/httpRequest.js b/httpRequest.js
deleted file mode 100644
index db696c65ee..0000000000
--- a/httpRequest.js
+++ /dev/null
@@ -1,43 +0,0 @@
-var request = require("request"),
- Parse = require('parse/node').Parse;
-
-module.exports = function(options) {
- var promise = new Parse.Promise();
- var callbacks = {
- success: options.success,
- error: options.error
- };
- delete options.success;
- delete options.error;
- if (options.uri && !options.url) {
- options.uri = options.url;
- delete options.url;
- }
- if (typeof options.body === 'object') {
- options.body = JSON.stringify(options.body);
- }
- request(options, (error, response, body) => {
- var httpResponse = {};
- httpResponse.status = response.statusCode;
- httpResponse.headers = response.headers;
- httpResponse.buffer = new Buffer(response.body);
- httpResponse.cookies = response.headers["set-cookie"];
- httpResponse.text = response.body;
- try {
- httpResponse.data = JSON.parse(response.body);
- } catch (e) {}
- // Consider <200 && >= 400 as errors
- if (error || httpResponse.status <200 || httpResponse.status >=400) {
- if (callbacks.error) {
- return callbacks.error(httpResponse);
- }
- return promise.reject(httpResponse);
- } else {
- if (callbacks.success) {
- return callbacks.success(httpResponse);
- }
- return promise.resolve(httpResponse);
- }
- });
- return promise;
-};
\ No newline at end of file
diff --git a/index.js b/index.js
deleted file mode 100644
index 37a88b893a..0000000000
--- a/index.js
+++ /dev/null
@@ -1,172 +0,0 @@
-// ParseServer - open-source compatible API Server for Parse apps
-
-var batch = require('./batch'),
- bodyParser = require('body-parser'),
- cache = require('./cache'),
- DatabaseAdapter = require('./DatabaseAdapter'),
- express = require('express'),
- FilesAdapter = require('./FilesAdapter'),
- S3Adapter = require('./S3Adapter'),
- middlewares = require('./middlewares'),
- multer = require('multer'),
- Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- httpRequest = require('./httpRequest');
-
-// Mutate the Parse object to add the Cloud Code handlers
-addParseCloud();
-
-// ParseServer works like a constructor of an express app.
-// The args that we understand are:
-// "databaseAdapter": a class like ExportAdapter providing create, find,
-// update, and delete
-// "filesAdapter": a class like GridStoreAdapter providing create, get,
-// and delete
-// "databaseURI": a uri like mongodb://localhost:27017/dbname to tell us
-// what database this Parse API connects to.
-// "cloud": relative location to cloud code to require, or a function
-// that is given an instance of Parse as a parameter. Use this instance of Parse
-// to register your cloud code hooks and functions.
-// "appId": the application id to host
-// "masterKey": the master key for requests to this app
-// "facebookAppIds": an array of valid Facebook Application IDs, required
-// if using Facebook login
-// "collectionPrefix": optional prefix for database collection names
-// "fileKey": optional key from Parse dashboard for supporting older files
-// hosted by Parse
-// "clientKey": optional key from Parse dashboard
-// "dotNetKey": optional key from Parse dashboard
-// "restAPIKey": optional key from Parse dashboard
-// "javascriptKey": optional key from Parse dashboard
-function ParseServer(args) {
- if (!args.appId || !args.masterKey) {
- throw 'You must provide an appId and masterKey!';
- }
-
- if (args.databaseAdapter) {
- DatabaseAdapter.setAdapter(args.databaseAdapter);
- }
- if (args.filesAdapter) {
- FilesAdapter.setAdapter(args.filesAdapter);
- }
- if (args.databaseURI) {
- DatabaseAdapter.setAppDatabaseURI(args.appId, args.databaseURI);
- }
- if (args.cloud) {
- addParseCloud();
- if (typeof args.cloud === 'function') {
- args.cloud(Parse)
- } else if (typeof args.cloud === 'string') {
- require(args.cloud);
- } else {
- throw "argument 'cloud' must either be a string or a function";
- }
-
- }
-
- cache.apps[args.appId] = {
- masterKey: args.masterKey,
- collectionPrefix: args.collectionPrefix || '',
- clientKey: args.clientKey || '',
- javascriptKey: args.javascriptKey || '',
- dotNetKey: args.dotNetKey || '',
- restAPIKey: args.restAPIKey || '',
- fileKey: args.fileKey || 'invalid-file-key',
- facebookAppIds: args.facebookAppIds || []
- };
-
- // To maintain compatibility. TODO: Remove in v2.1
- if (process.env.FACEBOOK_APP_ID) {
- cache.apps[args.appId]['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
- }
-
- // Initialize the node client SDK automatically
- Parse.initialize(args.appId, args.javascriptKey || '', args.masterKey);
- if(args.serverURL) {
- Parse.serverURL = args.serverURL;
- }
-
- // This app serves the Parse API directly.
- // It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
- var api = express();
-
- // File handling needs to be before default middlewares are applied
- api.use('/', require('./files').router);
-
- // TODO: separate this from the regular ParseServer object
- if (process.env.TESTING == 1) {
- console.log('enabling integration testing-routes');
- api.use('/', require('./testing-routes').router);
- }
-
- api.use(bodyParser.json({ 'type': '*/*' }));
- api.use(middlewares.allowCrossDomain);
- api.use(middlewares.allowMethodOverride);
- api.use(middlewares.handleParseHeaders);
-
- var router = new PromiseRouter();
-
- router.merge(require('./classes'));
- router.merge(require('./users'));
- router.merge(require('./sessions'));
- router.merge(require('./roles'));
- router.merge(require('./analytics'));
- router.merge(require('./push').router);
- router.merge(require('./installations'));
- router.merge(require('./functions'));
- router.merge(require('./schemas'));
-
- batch.mountOnto(router);
-
- router.mountOnto(api);
-
- api.use(middlewares.handleParseErrors);
-
- return api;
-}
-
-function addParseCloud() {
- Parse.Cloud.Functions = {};
- Parse.Cloud.Validators = {};
- Parse.Cloud.Triggers = {
- beforeSave: {},
- beforeDelete: {},
- afterSave: {},
- afterDelete: {}
- };
-
- Parse.Cloud.define = function(functionName, handler, validationHandler) {
- Parse.Cloud.Functions[functionName] = handler;
- Parse.Cloud.Validators[functionName] = validationHandler;
- };
- Parse.Cloud.beforeSave = function(parseClass, handler) {
- var className = getClassName(parseClass);
- Parse.Cloud.Triggers.beforeSave[className] = handler;
- };
- Parse.Cloud.beforeDelete = function(parseClass, handler) {
- var className = getClassName(parseClass);
- Parse.Cloud.Triggers.beforeDelete[className] = handler;
- };
- Parse.Cloud.afterSave = function(parseClass, handler) {
- var className = getClassName(parseClass);
- Parse.Cloud.Triggers.afterSave[className] = handler;
- };
- Parse.Cloud.afterDelete = function(parseClass, handler) {
- var className = getClassName(parseClass);
- Parse.Cloud.Triggers.afterDelete[className] = handler;
- };
- Parse.Cloud.httpRequest = httpRequest;
- global.Parse = Parse;
-}
-
-function getClassName(parseClass) {
- if (parseClass && parseClass.className) {
- return parseClass.className;
- }
- return parseClass;
-}
-
-module.exports = {
- ParseServer: ParseServer,
- S3Adapter: S3Adapter
-};
diff --git a/installations.js b/installations.js
deleted file mode 100644
index 517c3b812e..0000000000
--- a/installations.js
+++ /dev/null
@@ -1,80 +0,0 @@
-// installations.js
-
-var Parse = require('parse/node').Parse;
-var PromiseRouter = require('./PromiseRouter');
-var rest = require('./rest');
-
-var router = new PromiseRouter();
-
-
-// Returns a promise for a {status, response, location} object.
-function handleCreate(req) {
- return rest.create(req.config,
- req.auth, '_Installation', req.body);
-}
-
-// Returns a promise that resolves to a {response} object.
-function handleFind(req) {
- var options = {};
- if (req.body.skip) {
- options.skip = Number(req.body.skip);
- }
- if (req.body.limit) {
- options.limit = Number(req.body.limit);
- }
- if (req.body.order) {
- options.order = String(req.body.order);
- }
- if (req.body.count) {
- options.count = true;
- }
- if (req.body.include) {
- options.include = String(req.body.include);
- }
-
- return rest.find(req.config, req.auth,
- '_Installation', req.body.where, options)
- .then((response) => {
- return {response: response};
- });
-}
-
-// Returns a promise for a {response} object.
-function handleGet(req) {
- return rest.find(req.config, req.auth, '_Installation',
- {objectId: req.params.objectId})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
- 'Object not found.');
- } else {
- return {response: response.results[0]};
- }
- });
-}
-
-// Returns a promise for a {response} object.
-function handleUpdate(req) {
- return rest.update(req.config, req.auth,
- '_Installation', req.params.objectId, req.body)
- .then((response) => {
- return {response: response};
- });
-}
-
-// Returns a promise for a {response} object.
-function handleDelete(req) {
- return rest.del(req.config, req.auth,
- '_Installation', req.params.objectId)
- .then(() => {
- return {response: {}};
- });
-}
-
-router.route('POST','/installations', handleCreate);
-router.route('GET','/installations', handleFind);
-router.route('GET','/installations/:objectId', handleGet);
-router.route('PUT','/installations/:objectId', handleUpdate);
-router.route('DELETE','/installations/:objectId', handleDelete);
-
-module.exports = router;
\ No newline at end of file
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/middlewares.js b/middlewares.js
deleted file mode 100644
index bb2512391a..0000000000
--- a/middlewares.js
+++ /dev/null
@@ -1,192 +0,0 @@
-var Parse = require('parse/node').Parse;
-
-var auth = require('./Auth');
-var cache = require('./cache');
-var Config = require('./Config');
-
-// Checks that the request is authorized for this app and checks user
-// auth too.
-// The bodyparser should run before this middleware.
-// Adds info to the request:
-// req.config - the Config for this app
-// req.auth - the Auth for this request
-function handleParseHeaders(req, res, next) {
- var mountPathLength = req.originalUrl.length - req.url.length;
- var mountPath = req.originalUrl.slice(0, mountPathLength);
- var mount = req.protocol + '://' + req.get('host') + mountPath;
-
- var info = {
- appId: req.get('X-Parse-Application-Id'),
- sessionToken: req.get('X-Parse-Session-Token'),
- masterKey: req.get('X-Parse-Master-Key'),
- installationId: req.get('X-Parse-Installation-Id'),
- clientKey: req.get('X-Parse-Client-Key'),
- javascriptKey: req.get('X-Parse-Javascript-Key'),
- dotNetKey: req.get('X-Parse-Windows-Key'),
- restAPIKey: req.get('X-Parse-REST-API-Key')
- };
-
- var fileViaJSON = false;
-
- if (!info.appId || !cache.apps[info.appId]) {
- // See if we can find the app id on the body.
- if (req.body instanceof Buffer) {
- // The only chance to find the app id is if this is a file
- // upload that actually is a JSON body. So try to parse it.
- req.body = JSON.parse(req.body);
- fileViaJSON = true;
- }
-
- if (req.body && req.body._ApplicationId
- && cache.apps[req.body._ApplicationId]
- && (
- !info.masterKey
- ||
- cache.apps[req.body._ApplicationId]['masterKey'] === info.masterKey)
- ) {
- info.appId = req.body._ApplicationId;
- info.javascriptKey = req.body._JavaScriptKey || '';
- delete req.body._ApplicationId;
- delete req.body._JavaScriptKey;
- // TODO: test that the REST API formats generated by the other
- // SDKs are handled ok
- if (req.body._ClientVersion) {
- info.clientVersion = req.body._ClientVersion;
- delete req.body._ClientVersion;
- }
- if (req.body._InstallationId) {
- info.installationId = req.body._InstallationId;
- delete req.body._InstallationId;
- }
- if (req.body._SessionToken) {
- info.sessionToken = req.body._SessionToken;
- delete req.body._SessionToken;
- }
- if (req.body._MasterKey) {
- info.masterKey = req.body._MasterKey;
- delete req.body._MasterKey;
- }
- } else {
- return invalidRequest(req, res);
- }
- }
-
- if (fileViaJSON) {
- // We need to repopulate req.body with a buffer
- var base64 = req.body.base64;
- req.body = new Buffer(base64, 'base64');
- }
-
- info.app = cache.apps[info.appId];
- req.config = new Config(info.appId, mount);
- req.database = req.config.database;
- req.info = info;
-
- var isMaster = (info.masterKey === req.config.masterKey);
-
- if (isMaster) {
- req.auth = new auth.Auth(req.config, true);
- next();
- return;
- }
-
- // Client keys are not required in parse-server, but if any have been configured in the server, validate them
- // to preserve original behavior.
- var keyRequired = (req.config.clientKey
- || req.config.javascriptKey
- || req.config.dotNetKey
- || req.config.restAPIKey);
- var keyHandled = false;
- if (keyRequired
- && ((info.clientKey && req.config.clientKey && info.clientKey === req.config.clientKey)
- || (info.javascriptKey && req.config.javascriptKey && info.javascriptKey === req.config.javascriptKey)
- || (info.dotNetKey && req.config.dotNetKey && info.dotNetKey === req.config.dotNetKey)
- || (info.restAPIKey && req.config.restAPIKey && info.restAPIKey === req.config.restAPIKey)
- )) {
- keyHandled = true;
- }
- if (keyRequired && !keyHandled) {
- return invalidRequest(req, res);
- }
-
- if (!info.sessionToken) {
- req.auth = new auth.Auth(req.config, false);
- next();
- return;
- }
-
- return auth.getAuthForSessionToken(
- req.config, info.sessionToken).then((auth) => {
- if (auth) {
- req.auth = auth;
- next();
- }
- }).catch((error) => {
- // TODO: Determine the correct error scenario.
- console.log(error);
- throw new Parse.Error(Parse.Error.UNKNOWN_ERROR, error);
- });
-
-}
-
-var allowCrossDomain = function(req, res, next) {
- res.header('Access-Control-Allow-Origin', '*');
- res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
- res.header('Access-Control-Allow-Headers', '*');
-
- // intercept OPTIONS method
- if ('OPTIONS' == req.method) {
- res.send(200);
- }
- else {
- next();
- }
-};
-
-var allowMethodOverride = function(req, res, next) {
- if (req.method === 'POST' && req.body._method) {
- req.originalMethod = req.method;
- req.method = req.body._method;
- delete req.body._method;
- }
- next();
-};
-
-var handleParseErrors = function(err, req, res, next) {
- if (err instanceof Parse.Error) {
- var httpStatus;
-
- // TODO: fill out this mapping
- switch (err.code) {
- case Parse.Error.INTERNAL_SERVER_ERROR:
- httpStatus = 500;
- break;
- case Parse.Error.OBJECT_NOT_FOUND:
- httpStatus = 404;
- break;
- default:
- httpStatus = 400;
- }
-
- res.status(httpStatus);
- res.json({code: err.code, error: err.message});
- } else {
- console.log('Uncaught internal server error.', err, err.stack);
- res.status(500);
- res.json({code: Parse.Error.INTERNAL_SERVER_ERROR,
- message: 'Internal server error.'});
- }
-};
-
-function invalidRequest(req, res) {
- res.status(403);
- res.end('{"error":"unauthorized"}');
-}
-
-
-module.exports = {
- allowCrossDomain: allowCrossDomain,
- allowMethodOverride: allowMethodOverride,
- handleParseErrors: handleParseErrors,
- handleParseHeaders: handleParseHeaders
-};
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 3d145ee40b..7edb5697d1 100644
--- a/package.json
+++ b/package.json
@@ -1,46 +1,172 @@
{
"name": "parse-server",
- "version": "2.0.7",
+ "version": "9.9.0",
"description": "An express module providing a Parse-compatible API server",
- "main": "index.js",
+ "main": "lib/index.js",
"repository": {
"type": "git",
- "url": "https://github.com/ParsePlatform/parse-server"
+ "url": "https://github.com/parse-community/parse-server"
},
- "license": "BSD-3-Clause",
+ "files": [
+ "bin/",
+ "lib/",
+ "public/",
+ "views/",
+ "LICENSE",
+ "NOTICE",
+ "postinstall.js",
+ "README.md",
+ "types"
+ ],
+ "license": "Apache-2.0",
"dependencies": {
- "apn": "^1.7.5",
- "aws-sdk": "~2.2.33",
- "bcrypt-nodejs": "0.0.3",
- "body-parser": "^1.14.2",
- "deepcopy": "^0.6.1",
- "express": "^4.13.4",
- "hat": "~0.0.3",
- "mime": "^1.3.4",
- "mongodb": "~2.1.0",
- "multer": "^1.1.0",
- "parse": "^1.7.0",
- "randomstring": "^1.1.3",
- "node-gcm": "^0.14.0",
- "request": "^2.65.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",
+ "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": {
- "codecov": "^1.0.1",
- "deep-diff": "^0.3.3",
- "istanbul": "^0.4.2",
- "jasmine": "^2.3.2",
- "mongodb-runner": "^3.1.15"
+ "@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": {
- "pretest": "MONGODB_VERSION=${MONGODB_VERSION:=3.0.8} mongodb-runner start",
- "test": "NODE_ENV=test TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine",
- "posttest": "mongodb-runner stop",
- "start": "./bin/parse-server"
+ "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",
+ "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.1"
+ "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/password.js b/password.js
deleted file mode 100644
index f1154c96e6..0000000000
--- a/password.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Tools for encrypting and decrypting passwords.
-// Basically promise-friendly wrappers for bcrypt.
-var bcrypt = require('bcrypt-nodejs');
-
-// Returns a promise for a hashed password string.
-function hash(password) {
- return new Promise(function(fulfill, reject) {
- bcrypt.hash(password, null, null, function(err, hashedPassword) {
- if (err) {
- reject(err);
- } else {
- fulfill(hashedPassword);
- }
- });
- });
-}
-
-// Returns a promise for whether this password compares to equal this
-// hashed password.
-function compare(password, hashedPassword) {
- return new Promise(function(fulfill, reject) {
- bcrypt.compare(password, hashedPassword, function(err, success) {
- if (err) {
- reject(err);
- } else {
- fulfill(success);
- }
- });
- });
-}
-
-module.exports = {
- hash: hash,
- compare: compare
-};
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/push.js b/push.js
deleted file mode 100644
index 29a6a944e5..0000000000
--- a/push.js
+++ /dev/null
@@ -1,124 +0,0 @@
-// push.js
-
-var Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- rest = require('./rest');
-
-var validPushTypes = ['ios', 'android'];
-
-function handlePushWithoutQueue(req) {
- validateMasterKey(req);
- var where = getQueryCondition(req);
- validateDeviceType(where);
- // Replace the expiration_time with a valid Unix epoch milliseconds time
- req.body['expiration_time'] = getExpirationTime(req);
- return rest.find(req.config, req.auth, '_Installation', where).then(function(response) {
- throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE,
- 'This path is not implemented yet.');
- });
-}
-
-/**
- * Check whether the deviceType parameter in qury condition is valid or not.
- * @param {Object} where A query condition
- */
-function validateDeviceType(where) {
- var where = where || {};
- var deviceTypeField = where.deviceType || {};
- var deviceTypes = [];
- if (typeof deviceTypeField === 'string') {
- deviceTypes.push(deviceTypeField);
- } else if (typeof deviceTypeField['$in'] === 'array') {
- deviceTypes.concat(deviceTypeField['$in']);
- }
- for (var i = 0; i < deviceTypes.length; i++) {
- var deviceType = deviceTypes[i];
- if (validPushTypes.indexOf(deviceType) < 0) {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- deviceType + ' is not supported push type.');
- }
- }
-}
-
-/**
- * Get expiration time from the request body.
- * @param {Object} request A request object
- * @returns {Number|undefined} The expiration time if it exists in the request
- */
-function getExpirationTime(req) {
- var body = req.body || {};
- var hasExpirationTime = !!body['expiration_time'];
- if (!hasExpirationTime) {
- return;
- }
- var expirationTimeParam = body['expiration_time'];
- var expirationTime;
- if (typeof expirationTimeParam === 'number') {
- expirationTime = new Date(expirationTimeParam * 1000);
- } else if (typeof expirationTimeParam === 'string') {
- expirationTime = new Date(expirationTimeParam);
- } else {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- body['expiration_time'] + ' is not valid time.');
- }
- // Check expirationTime is valid or not, if it is not valid, expirationTime is NaN
- if (!isFinite(expirationTime)) {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- body['expiration_time'] + ' is not valid time.');
- }
- return expirationTime.valueOf();
-}
-
-/**
- * Get query condition from the request body.
- * @param {Object} request A request object
- * @returns {Object} The query condition, the where field in a query api call
- */
-function getQueryCondition(req) {
- var body = req.body || {};
- var hasWhere = typeof body.where !== 'undefined';
- var hasChannels = typeof body.channels !== 'undefined';
-
- var where;
- if (hasWhere && hasChannels) {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- 'Channels and query can not be set at the same time.');
- } else if (hasWhere) {
- where = body.where;
- } else if (hasChannels) {
- where = {
- "channels": {
- "$in": body.channels
- }
- }
- } else {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- 'Channels and query should be set at least one.');
- }
- return where;
-}
-
-/**
- * Check whether the api call has master key or not.
- * @param {Object} request A request object
- */
-function validateMasterKey(req) {
- if (req.info.masterKey !== req.config.masterKey) {
- throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
- 'Master key is invalid, you should only use master key to send push');
- }
-}
-
-var router = new PromiseRouter();
-router.route('POST','/push', handlePushWithoutQueue);
-
-module.exports = {
- router: router
-}
-
-if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
- module.exports.getQueryCondition = getQueryCondition;
- module.exports.validateMasterKey = validateMasterKey;
- module.exports.getExpirationTime = getExpirationTime;
- module.exports.validateDeviceType = validateDeviceType;
-}
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/rest.js b/rest.js
deleted file mode 100644
index 552fa6be8c..0000000000
--- a/rest.js
+++ /dev/null
@@ -1,129 +0,0 @@
-// This file contains helpers for running operations in REST format.
-// The goal is that handlers that explicitly handle an express route
-// should just be shallow wrappers around things in this file, but
-// these functions should not explicitly depend on the request
-// object.
-// This means that one of these handlers can support multiple
-// routes. That's useful for the routes that do really similar
-// things.
-
-var Parse = require('parse/node').Parse;
-
-var cache = require('./cache');
-var RestQuery = require('./RestQuery');
-var RestWrite = require('./RestWrite');
-var triggers = require('./triggers');
-
-// Returns a promise for an object with optional keys 'results' and 'count'.
-function find(config, auth, className, restWhere, restOptions) {
- enforceRoleSecurity('find', className, auth);
- var query = new RestQuery(config, auth, className,
- restWhere, restOptions);
- return query.execute();
-}
-
-// Returns a promise that doesn't resolve to any useful value.
-function del(config, auth, className, objectId) {
- if (typeof objectId !== 'string') {
- throw new Parse.Error(Parse.Error.INVALID_JSON,
- 'bad objectId');
- }
-
- if (className === '_User' && !auth.couldUpdateUserId(objectId)) {
- throw new Parse.Error(Parse.Error.SESSION_MISSING,
- 'insufficient auth to delete user');
- }
-
- enforceRoleSecurity('delete', className, auth);
-
- var inflatedObject;
-
- return Promise.resolve().then(() => {
- if (triggers.getTrigger(className, 'beforeDelete') ||
- triggers.getTrigger(className, 'afterDelete') ||
- className == '_Session') {
- return find(config, auth, className, {objectId: objectId})
- .then((response) => {
- if (response && response.results && response.results.length) {
- response.results[0].className = className;
- cache.clearUser(response.results[0].sessionToken);
- inflatedObject = Parse.Object.fromJSON(response.results[0]);
- return triggers.maybeRunTrigger('beforeDelete',
- auth, inflatedObject);
- }
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
- 'Object not found for delete.');
- });
- }
- return Promise.resolve({});
- }).then(() => {
- var options = {};
- if (!auth.isMaster) {
- options.acl = ['*'];
- if (auth.user) {
- options.acl.push(auth.user.id);
- }
- }
-
- return config.database.destroy(className, {
- objectId: objectId
- }, options);
- }).then(() => {
- triggers.maybeRunTrigger('afterDelete', auth, inflatedObject);
- return Promise.resolve();
- });
-}
-
-// Returns a promise for a {response, status, location} object.
-function create(config, auth, className, restObject) {
- enforceRoleSecurity('create', className, auth);
-
- var write = new RestWrite(config, auth, className, null, restObject);
- return write.execute();
-}
-
-// Returns a promise that contains the fields of the update that the
-// REST API is supposed to return.
-// Usually, this is just updatedAt.
-function update(config, auth, className, objectId, restObject) {
- enforceRoleSecurity('update', className, auth);
-
- return Promise.resolve().then(() => {
- if (triggers.getTrigger(className, 'beforeSave') ||
- triggers.getTrigger(className, 'afterSave')) {
- return find(config, auth, className, {objectId: objectId});
- }
- return Promise.resolve({});
- }).then((response) => {
- var originalRestObject;
- if (response && response.results && response.results.length) {
- originalRestObject = response.results[0];
- }
-
- var write = new RestWrite(config, auth, className,
- {objectId: objectId}, restObject, originalRestObject);
- return write.execute();
- });
-}
-
-// Disallowing access to the _Role collection except by master key
-function enforceRoleSecurity(method, className, auth) {
- if (className === '_Role' && !auth.isMaster) {
- throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN,
- 'Clients aren\'t allowed to perform the ' +
- method + ' operation on the role collection.');
- }
- if (method === 'delete' && className === '_Installation' && !auth.isMaster) {
- throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN,
- 'Clients aren\'t allowed to perform the ' +
- 'delete operation on the installation collection.');
-
- }
-}
-
-module.exports = {
- create: create,
- del: del,
- find: find,
- update: update
-};
diff --git a/roles.js b/roles.js
deleted file mode 100644
index 6aaf806526..0000000000
--- a/roles.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// roles.js
-
-var Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- rest = require('./rest');
-
-var router = new PromiseRouter();
-
-function handleCreate(req) {
- return rest.create(req.config, req.auth,
- '_Role', req.body);
-}
-
-function handleUpdate(req) {
- return rest.update(req.config, req.auth, '_Role',
- req.params.objectId, req.body)
- .then((response) => {
- return {response: response};
- });
-}
-
-function handleDelete(req) {
- return rest.del(req.config, req.auth,
- '_Role', req.params.objectId)
- .then(() => {
- return {response: {}};
- });
-}
-
-function handleGet(req) {
- return rest.find(req.config, req.auth, '_Role',
- {objectId: req.params.objectId})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
- 'Object not found.');
- } else {
- return {response: response.results[0]};
- }
- });
-}
-
-router.route('POST','/roles', handleCreate);
-router.route('GET','/roles/:objectId', handleGet);
-router.route('PUT','/roles/:objectId', handleUpdate);
-router.route('DELETE','/roles/:objectId', handleDelete);
-
-module.exports = router;
\ No newline at end of file
diff --git a/schemas.js b/schemas.js
deleted file mode 100644
index 6145b7e6cf..0000000000
--- a/schemas.js
+++ /dev/null
@@ -1,131 +0,0 @@
-// schemas.js
-
-var express = require('express'),
- Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- Schema = require('./Schema');
-
-var router = new PromiseRouter();
-
-function mongoFieldTypeToSchemaAPIType(type) {
- if (type[0] === '*') {
- return {
- type: 'Pointer',
- targetClass: type.slice(1),
- };
- }
- if (type.startsWith('relation<')) {
- return {
- type: 'Relation',
- targetClass: type.slice('relation<'.length, type.length - 1),
- };
- }
- switch (type) {
- case 'number': return {type: 'Number'};
- case 'string': return {type: 'String'};
- case 'boolean': return {type: 'Boolean'};
- case 'date': return {type: 'Date'};
- case 'map':
- case 'object': return {type: 'Object'};
- case 'array': return {type: 'Array'};
- case 'geopoint': return {type: 'GeoPoint'};
- case 'file': return {type: 'File'};
- }
-}
-
-function mongoSchemaAPIResponseFields(schema) {
- fieldNames = Object.keys(schema).filter(key => key !== '_id' && key !== '_metadata');
- response = fieldNames.reduce((obj, fieldName) => {
- obj[fieldName] = mongoFieldTypeToSchemaAPIType(schema[fieldName])
- return obj;
- }, {});
- response.ACL = {type: 'ACL'};
- response.createdAt = {type: 'Date'};
- response.updatedAt = {type: 'Date'};
- response.objectId = {type: 'String'};
- return response;
-}
-
-function mongoSchemaToSchemaAPIResponse(schema) {
- return {
- className: schema._id,
- fields: mongoSchemaAPIResponseFields(schema),
- };
-}
-
-function getAllSchemas(req) {
- if (!req.auth.isMaster) {
- return Promise.resolve({
- status: 401,
- response: {error: 'master key not specified'},
- });
- }
- return req.config.database.collection('_SCHEMA')
- .then(coll => coll.find({}).toArray())
- .then(schemas => ({response: {
- results: schemas.map(mongoSchemaToSchemaAPIResponse)
- }}));
-}
-
-function getOneSchema(req) {
- if (!req.auth.isMaster) {
- return Promise.resolve({
- status: 401,
- response: {error: 'unauthorized'},
- });
- }
- return req.config.database.collection('_SCHEMA')
- .then(coll => coll.findOne({'_id': req.params.className}))
- .then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)}))
- .catch(() => ({
- status: 400,
- response: {
- code: 103,
- error: 'class ' + req.params.className + ' does not exist',
- }
- }));
-}
-
-function createSchema(req) {
- if (!req.auth.isMaster) {
- return Promise.resolve({
- status: 401,
- response: {error: 'master key not specified'},
- });
- }
- if (req.params.className && req.body.className) {
- if (req.params.className != req.body.className) {
- return Promise.resolve({
- status: 400,
- response: {
- code: Parse.Error.INVALID_CLASS_NAME,
- error: 'class name mismatch between ' + req.body.className + ' and ' + req.params.className,
- },
- });
- }
- }
- var className = req.params.className || req.body.className;
- if (!className) {
- return Promise.resolve({
- status: 400,
- response: {
- code: 135,
- error: 'POST ' + req.path + ' needs class name',
- },
- });
- }
- return req.config.database.loadSchema()
- .then(schema => schema.addClassIfNotExists(className, req.body.fields))
- .then(result => ({ response: mongoSchemaToSchemaAPIResponse(result) }))
- .catch(error => ({
- status: 400,
- response: error,
- }));
-}
-
-router.route('GET', '/schemas', getAllSchemas);
-router.route('GET', '/schemas/:className', getOneSchema);
-router.route('POST', '/schemas', createSchema);
-router.route('POST', '/schemas/:className', createSchema);
-
-module.exports = router;
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/sessions.js b/sessions.js
deleted file mode 100644
index 30290a9d52..0000000000
--- a/sessions.js
+++ /dev/null
@@ -1,122 +0,0 @@
-// sessions.js
-
-var Auth = require('./Auth'),
- Parse = require('parse/node').Parse,
- PromiseRouter = require('./PromiseRouter'),
- rest = require('./rest');
-
-var router = new PromiseRouter();
-
-function handleCreate(req) {
- return rest.create(req.config, req.auth,
- '_Session', req.body);
-}
-
-function handleUpdate(req) {
- return rest.update(req.config, req.auth, '_Session',
- req.params.objectId, req.body)
- .then((response) => {
- return {response: response};
- });
-}
-
-function handleDelete(req) {
- return rest.del(req.config, req.auth,
- '_Session', req.params.objectId)
- .then(() => {
- return {response: {}};
- });
-}
-
-function handleGet(req) {
- return rest.find(req.config, req.auth, '_Session',
- {objectId: req.params.objectId})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
- 'Object not found.');
- } else {
- return {response: response.results[0]};
- }
- });
-}
-
-function handleLogout(req) {
- // TODO: Verify correct behavior for logout without token
- if (!req.info || !req.info.sessionToken) {
- throw new Parse.Error(Parse.Error.SESSION_MISSING,
- 'Session token required for logout.');
- }
- return rest.find(req.config, Auth.master(req.config), '_Session',
- { _session_token: req.info.sessionToken})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN,
- 'Session token not found.');
- }
- return rest.del(req.config, Auth.master(req.config), '_Session',
- response.results[0].objectId);
- }).then(() => {
- return {
- status: 200,
- response: {}
- };
- });
-}
-
-function handleFind(req) {
- var options = {};
- if (req.body.skip) {
- options.skip = Number(req.body.skip);
- }
- if (req.body.limit) {
- options.limit = Number(req.body.limit);
- }
- if (req.body.order) {
- options.order = String(req.body.order);
- }
- if (req.body.count) {
- options.count = true;
- }
- if (typeof req.body.keys == 'string') {
- options.keys = req.body.keys;
- }
- if (req.body.include) {
- options.include = String(req.body.include);
- }
-
- return rest.find(req.config, req.auth,
- '_Session', req.body.where, options)
- .then((response) => {
- return {response: response};
- });
-}
-
-function handleMe(req) {
- // TODO: Verify correct behavior
- if (!req.info || !req.info.sessionToken) {
- throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN,
- 'Session token required.');
- }
- return rest.find(req.config, Auth.master(req.config), '_Session',
- { _session_token: req.info.sessionToken})
- .then((response) => {
- if (!response.results || response.results.length == 0) {
- throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN,
- 'Session token not found.');
- }
- return {
- response: response.results[0]
- };
- });
-}
-
-router.route('POST', '/logout', handleLogout);
-router.route('POST','/sessions', handleCreate);
-router.route('GET','/sessions/me', handleMe);
-router.route('GET','/sessions/:objectId', handleGet);
-router.route('PUT','/sessions/:objectId', handleUpdate);
-router.route('GET','/sessions', handleFind);
-router.route('DELETE','/sessions/:objectId', handleDelete);
-
-module.exports = router;
\ No newline at end of file
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/APNS.spec.js b/spec/APNS.spec.js
deleted file mode 100644
index c50bb5c952..0000000000
--- a/spec/APNS.spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-var APNS = require('../APNS');
-
-describe('APNS', () => {
- it('can generate APNS notification', (done) => {
- //Mock request data
- var data = {
- 'alert': 'alert',
- 'badge': 100,
- 'sound': 'test',
- 'content-available': 1,
- 'category': 'INVITE_CATEGORY',
- 'key': 'value',
- 'keyAgain': 'valueAgain'
- };
- var expirationTime = 1454571491354
-
- var notification = APNS.generateNotification(data, expirationTime);
-
- expect(notification.alert).toEqual(data.alert);
- expect(notification.badge).toEqual(data.badge);
- expect(notification.sound).toEqual(data.sound);
- expect(notification.contentAvailable).toEqual(1);
- expect(notification.category).toEqual(data.category);
- expect(notification.payload).toEqual({
- 'key': 'value',
- 'keyAgain': 'valueAgain'
- });
- expect(notification.expiry).toEqual(expirationTime);
- done();
- });
-
- it('can send APNS notification', (done) => {
- var apns = new APNS();
- var sender = {
- pushNotification: jasmine.createSpy('send')
- };
- apns.sender = sender;
- // Mock data
- var expirationTime = 1454571491354
- var data = {
- 'expiration_time': expirationTime,
- 'data': {
- 'alert': 'alert'
- }
- }
- // Mock registrationTokens
- var deviceTokens = ['token'];
-
- var promise = apns.send(data, deviceTokens);
- expect(sender.pushNotification).toHaveBeenCalled();
- var args = sender.pushNotification.calls.first().args;
- var notification = args[0];
- expect(notification.alert).toEqual(data.data.alert);
- expect(notification.expiry).toEqual(data['expiration_time']);
- expect(args[1]).toEqual(deviceTokens);
- done();
- });
-});
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
new file mode 100644
index 0000000000..4cda42e162
--- /dev/null
+++ b/spec/AdaptableController.spec.js
@@ -0,0 +1,87 @@
+const AdaptableController = require('../lib/Controllers/AdaptableController').AdaptableController;
+const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default;
+const FilesController = require('../lib/Controllers/FilesController').FilesController;
+
+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 => {
+ 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';
+ expect(controller.adapter).toBe(adapter);
+ done();
+ });
+
+ 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() {}
+ 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() {}
+ const adapter = new WrongAdapter();
+ expect(() => {
+ new FilesController(adapter);
+ }).toThrow();
+ done();
+ });
+
+ it('should fail to instantiate a controller without an adapter', done => {
+ expect(() => {
+ new FilesController();
+ }).toThrow();
+ done();
+ });
+
+ 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 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 () {};
+
+ const adapter = new AGoodAdapter();
+ expect(() => {
+ new FilesController(adapter);
+ }).not.toThrow();
+ done();
+ });
+});
diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js
new file mode 100644
index 0000000000..8d33bf2094
--- /dev/null
+++ b/spec/AdapterLoader.spec.js
@@ -0,0 +1,184 @@
+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');
+
+describe('AdapterLoader', () => {
+ it('should instantiate an adapter from string in object', done => {
+ const adapterPath = require('path').resolve('./spec/support/MockAdapter');
+
+ const adapter = loadAdapter({
+ adapter: adapterPath,
+ options: {
+ key: 'value',
+ foo: '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 => {
+ const adapterPath = require('path').resolve('./spec/support/MockAdapter');
+ const adapter = loadAdapter(adapterPath);
+
+ expect(Utils.isObject(adapter)).toBe(true);
+ done();
+ });
+
+ 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');
+ 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 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 => {
+ const adapter = loadAdapter(null, FilesAdapter);
+ expect(adapter instanceof FilesAdapter).toBe(true);
+ done();
+ });
+
+ 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 => {
+ const originalAdapter = new FilesAdapter();
+ const adapter = loadAdapter(originalAdapter);
+ expect(adapter).toBe(originalAdapter);
+ done();
+ });
+
+ it('should fail loading an improperly configured adapter', done => {
+ const Adapter = function (options) {
+ if (!options.foo) {
+ throw 'foo is required for that adapter';
+ }
+ };
+ const adapterOptions = {
+ param: 'key',
+ doSomething: function () {},
+ };
+
+ expect(() => {
+ const adapter = loadAdapter(adapterOptions, Adapter);
+ expect(adapter).toEqual(adapterOptions);
+ }).not.toThrow('foo is required for that adapter');
+ done();
+ });
+
+ 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(() => {
+ 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 file adapter from direct passing', done => {
+ spyOn(console, 'warn').and.callFake(() => {});
+ const mockFilesAdapter = new MockFilesAdapter('key', 'secret', 'bucket');
+ expect(() => {
+ 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
new file mode 100644
index 0000000000..a055cda5bc
--- /dev/null
+++ b/spec/Auth.spec.js
@@ -0,0 +1,256 @@
+'use strict';
+
+describe('Auth', () => {
+ const { Auth, getAuthForSessionToken } = require('../lib/Auth.js');
+ const Config = require('../lib/Config');
+ describe('getUserRoles', () => {
+ let auth;
+ let config;
+ let currentRoles = null;
+ const currentUserId = 'userId';
+
+ beforeEach(() => {
+ currentRoles = ['role:userId'];
+
+ config = {
+ cacheController: {
+ role: {
+ get: () => Promise.resolve(currentRoles),
+ set: jasmine.createSpy('set'),
+ },
+ },
+ };
+ spyOn(config.cacheController.role, 'get').and.callThrough();
+
+ auth = new Auth({
+ config: config,
+ isMaster: false,
+ user: {
+ id: currentUserId,
+ },
+ installationId: 'installationId',
+ });
+ });
+
+ 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);
+
+ 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 => {
+ const loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough();
+ auth
+ .getUserRoles()
+ .then(roles => {
+ expect(roles).toEqual(currentRoles);
+ return auth.getUserRoles();
+ })
+ .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);
+
+ 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([]))
+ .then(() => done());
+ });
+
+ 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
new file mode 100644
index 0000000000..39061b8083
--- /dev/null
+++ b/spec/CLI.spec.js
@@ -0,0 +1,308 @@
+'use strict';
+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');
+
+const testDefinitions = {
+ arg0: 'PROGRAM_ARG_0',
+ arg1: {
+ env: 'PROGRAM_ARG_1',
+ required: true,
+ },
+ arg2: {
+ env: 'PROGRAM_ARG_2',
+ action: function (value) {
+ const intValue = parseInt(value);
+ if (!Number.isInteger(intValue)) {
+ throw 'arg2 is invalid';
+ }
+ return intValue;
+ },
+ },
+ arg3: {},
+ arg4: {
+ default: 'arg4Value',
+ },
+};
+
+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;
+ delete commander.arg2;
+ delete commander.arg3;
+ delete commander.arg4;
+ done();
+ });
+
+ 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');
+ done();
+ });
+
+ 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',
+ });
+ expect(commander.arg0).toEqual('arg0ENVValue');
+ expect(commander.arg1).toEqual('arg1ENVValue');
+ expect(commander.arg2).toEqual(3);
+ expect(commander.arg4).toEqual('arg4Value');
+ done();
+ });
+
+ 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.arg2).toEqual(4);
+ expect(commander.arg4).toEqual('');
+ });
+
+ 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');
+ done();
+ });
+
+ 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.arg4).toBe('/1');
+ done();
+ });
+
+ 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');
+ done();
+ });
+
+ 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');
+ done();
+ });
+
+ 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.arg4).toBe('/1');
+ done();
+ });
+
+ 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');
+ 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
new file mode 100644
index 0000000000..de07126214
--- /dev/null
+++ b/spec/CacheController.spec.js
@@ -0,0 +1,70 @@
+const CacheController = require('../lib/Controllers/CacheController.js').default;
+
+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'),
+ };
+
+ spyOn(FakeCacheAdapter, 'get').and.callThrough();
+ });
+
+ 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);
+ expect(cache.user).not.toEqual(null);
+ expect(cache.user.get).not.toEqual(null);
+
+ done();
+ });
+
+ ['role', 'user'].forEach(cacheName => {
+ it('should prefix ' + cacheName + ' cache', () => {
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName];
+
+ cache.put(KEY, 'world');
+ const firstPut = FakeCacheAdapter.put.calls.first();
+ expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
+
+ cache.get(KEY);
+ const firstGet = FakeCacheAdapter.get.calls.first();
+ expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
+
+ cache.del(KEY);
+ const firstDel = FakeCacheAdapter.del.calls.first();
+ expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
+ });
+ });
+
+ it('should clear the entire cache', () => {
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID);
+
+ cache.clear();
+ expect(FakeCacheAdapter.clear.calls.count()).toEqual(1);
+
+ cache.user.clear();
+ expect(FakeCacheAdapter.clear.calls.count()).toEqual(2);
+
+ cache.role.clear();
+ expect(FakeCacheAdapter.clear.calls.count()).toEqual(3);
+ });
+
+ it('should handle cache rejections', done => {
+ FakeCacheAdapter.get = () => Promise.reject();
+
+ const cache = new CacheController(FakeCacheAdapter, FakeAppID);
+
+ cache.get('foo').then(done, () => {
+ fail('Promise should not be rejected.');
+ });
+ });
+});
diff --git a/spec/Client.spec.js b/spec/Client.spec.js
new file mode 100644
index 0000000000..0de226204a
--- /dev/null
+++ b/spec/Client.spec.js
@@ -0,0 +1,288 @@
+const Client = require('../lib/LiveQuery/Client').Client;
+const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').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 () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ Client.pushResponse(parseWebSocket, 'message');
+
+ expect(parseWebSocket.send).toHaveBeenCalledWith('message');
+ });
+
+ it('can push error', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ Client.pushError(parseWebSocket, 1, 'error', true);
+
+ 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 () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
+ subscription: subscription,
+ 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 () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
+ subscription: subscription,
+ fields: fields,
+ };
+ const client = new Client(1, {});
+ client.addSubscriptionInfo(1, subscriptionInfo);
+ const subscriptionInfoAgain = client.getSubscriptionInfo(1);
+
+ expect(subscriptionInfoAgain).toBe(subscriptionInfo);
+ });
+
+ it('can delete subscription information', function () {
+ const subscription = {};
+ const fields = ['test'];
+ const subscriptionInfo = {
+ subscription: subscription,
+ 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 () {
+ const parseObjectJSON = {
+ key: 'value',
+ className: 'test',
+ objectId: 'test',
+ updatedAt: '2015-12-07T21:27:13.746Z',
+ createdAt: '2015-12-07T21:27:13.746Z',
+ ACL: 'test',
+ };
+ const client = new Client(1, {});
+
+ expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON);
+ });
+
+ 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',
+ };
+ const client = new Client(1, {});
+
+ expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON);
+ });
+
+ 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',
+ };
+ const client = new Client(1, {});
+
+ expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({
+ className: 'test',
+ objectId: 'test',
+ updatedAt: '2015-12-07T21:27:13.746Z',
+ createdAt: '2015-12-07T21:27:13.746Z',
+ ACL: 'test',
+ test: 'test',
+ });
+ });
+
+ 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',
+ };
+ const client = new Client(1, {});
+ const limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']);
+
+ expect(limitedParseObject).toEqual({
+ className: 'test',
+ objectId: 'test',
+ updatedAt: '2015-12-07T21:27:13.746Z',
+ createdAt: '2015-12-07T21:27:13.746Z',
+ ACL: 'test',
+ });
+ expect('name' in limitedParseObject).toBe(false);
+ });
+
+ it('can push connect response', function () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushConnect();
+
+ 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 () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushSubscribe(2);
+
+ 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 () {
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushUnsubscribe(2);
+
+ 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 () {
+ 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',
+ };
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushCreate(2, parseObjectJSON);
+
+ 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 () {
+ 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',
+ };
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushEnter(2, parseObjectJSON);
+
+ 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 () {
+ 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',
+ };
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushUpdate(2, parseObjectJSON);
+
+ 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 () {
+ 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',
+ };
+ const parseWebSocket = {
+ send: jasmine.createSpy('send'),
+ };
+ const client = new Client(1, parseWebSocket);
+ client.pushLeave(2, parseObjectJSON);
+
+ 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);
+ expect(messageJSON.object).toEqual(parseObjectJSON);
+ });
+});
diff --git a/spec/ClientSDK.spec.js b/spec/ClientSDK.spec.js
new file mode 100644
index 0000000000..987770833c
--- /dev/null
+++ b/spec/ClientSDK.spec.js
@@ -0,0 +1,49 @@
+const ClientSDK = require('../lib/ClientSDK');
+
+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);
+
+ 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
new file mode 100644
index 0000000000..941d896aae
--- /dev/null
+++ b/spec/CloudCode.spec.js
@@ -0,0 +1,5110 @@
+'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', {}).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', {}).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', () => {
+ return 'Hello world!';
+ });
+
+ Parse.Cloud.run('hello', {}).then(result => {
+ expect(result).toEqual('Hello world!');
+ 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('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('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!';
+ });
+ 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('is cleared cleared after the previous test', done => {
+ Parse.Cloud.run('hello', {}).catch(error => {
+ expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED);
+ done();
+ });
+ });
+
+ it('basic beforeSave rejection', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFail', function () {
+ throw new Error('You shall not pass!');
+ });
+
+ const 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('returns an error', done => {
+ Parse.Cloud.define('cloudCodeWithError', () => {
+ /* eslint-disable no-undef */
+ foo.bar();
+ /* eslint-enable no-undef */
+ return 'I better throw an error.';
+ });
+
+ 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('returns an empty error', done => {
+ Parse.Cloud.define('cloudCodeWithError', () => {
+ throw null;
+ });
+
+ 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();
+ }
+ );
+ });
+
+ it('beforeFind can throw string', async function (done) {
+ Parse.Cloud.beforeFind('beforeFind', () => {
+ throw 'throw beforeFind';
+ });
+ const obj = new Parse.Object('beforeFind');
+ obj.set('foo', 'bar');
+ 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();
+ }
+ });
+
+ describe('beforeFind without DB operations', () => {
+ let findSpy;
+
+ beforeEach(() => {
+ const config = Config.get('test');
+ const databaseAdapter = config.database.adapter;
+ findSpy = spyOn(databaseAdapter, 'find').and.callThrough();
+ });
+
+ 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');
+ });
+
+ 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('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');
+ });
+
+ 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('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');
+ });
+
+ const testObj = new Parse.Object('TestObject');
+ await testObj.save();
+ findSpy.calls.reset();
+
+ 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();
+ });
+
+ 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);
+ });
+
+ 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();
+ });
+ });
+
+ 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];
+ });
+ });
+
+ 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('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');
+ });
+
+ 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('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);
+ });
+
+ 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');
+
+ describe('maybeRunAfterFindTrigger - direct function tests', () => {
+ const testConfig = {
+ applicationId: 'test',
+ logLevels: { triggerBeforeSuccess: 'info', triggerAfter: 'info' },
+ };
+
+ it('should convert Parse.Object instances to JSON when no trigger defined', async () => {
+ const className = 'TestParseObjectDirect_' + Date.now();
+
+ 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('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([]);
+ });
+
+ 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('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();
+ });
+
+ 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('beforeSave rejection with custom error code', function (done) {
+ Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
+ throw new Parse.Error(999, 'Nope');
+ });
+
+ const 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();
+ }
+ );
+ });
+
+ 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();
+ }
+ );
+ });
+
+ 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();
+ }
+ );
+ });
+
+ 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) {
+ 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('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');
+ });
+ 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();
+ });
+ 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('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 });
+ });
+
+ 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();
+ }
+ });
+
+ 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('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);
+ });
+
+ 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();
+ }
+ });
+});
+
+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);
+ })
+ ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized'));
+
+ 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': 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',
+ },
+ });
+ 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();
+ }
+ 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_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('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');
+ }
+ });
+
+ 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');
+ }
+ });
+
+ 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.');
+ }
+ });
+
+ 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);
+ });
+
+ 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_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');
+ }
+ });
+});
+
+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);
+ });
+
+ 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.'
+ );
+ });
+});
+
+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',
+ });
+
+ 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
new file mode 100644
index 0000000000..16d9d02950
--- /dev/null
+++ b/spec/CloudCodeLogger.spec.js
@@ -0,0 +1,404 @@
+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
new file mode 100644
index 0000000000..11a901f399
--- /dev/null
+++ b/spec/EmailVerificationToken.spec.js
@@ -0,0 +1,1303 @@
+'use strict';
+
+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('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: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 0.5, // 0.5 second
+ 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('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: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 0.5, // 0.5 second
+ 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_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: () => {},
+ };
+ 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;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ expect(response.text).toContain('Email verified!');
+ });
+
+ 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: () => {},
+ };
+ 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;
+ const response = await request({
+ url: sendEmailOptions.link,
+ followRedirects: false,
+ });
+ expect(response.status).toEqual(200);
+ await user.fetch();
+ expect(user.get('emailVerified')).toEqual(true);
+ });
+
+ 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: () => {},
+ };
+ 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;
+ 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_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: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ 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_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => {
+ let sendEmailOptions;
+ const emailAdapter = {
+ sendVerificationEmail: options => {
+ sendEmailOptions = options;
+ },
+ 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' });
+ 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',
+ 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_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: () => {},
+ };
+ 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',
+ };
+
+ // setup server WITHOUT enabling the expire email verify token flag
+ 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('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: () => {},
+ };
+ const serverConfig = {
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ publicServerURL: 'http://localhost:8378/1',
+ };
+
+ // setup server WITHOUT enabling the expire email verify token flag
+ 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_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();
+
+ 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',
+ };
+
+ 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 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, // 5 seconds
+ 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',
+ }),
+ });
+
+ 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',
+ });
+ 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_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: () => {},
+ };
+ await reconfigureServer({
+ appName: 'emailVerifyToken',
+ verifyUserEmails: true,
+ emailAdapter: emailAdapter,
+ emailVerifyTokenValidityDuration: 5, // 5 seconds
+ 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(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
new file mode 100644
index 0000000000..00358646de
--- /dev/null
+++ b/spec/EventEmitterPubSub.spec.js
@@ -0,0 +1,43 @@
+const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
+
+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
+ let isChecked = false;
+ subscriber.on('message', function (channel, message) {
+ isChecked = true;
+ expect(channel).toBe('testChannel');
+ expect(message).toBe('testMessage');
+ });
+
+ publisher.publish('testChannel', 'testMessage');
+ // Make sure the callback is checked
+ expect(isChecked).toBe(true);
+ });
+
+ it('can unsubscribe', function () {
+ const publisher = EventEmitterPubSub.createPublisher();
+ const subscriber = EventEmitterPubSub.createSubscriber();
+ subscriber.subscribe('testChannel');
+ subscriber.unsubscribe('testChannel');
+ // Register mock checked for subscriber
+ let isCalled = false;
+ subscriber.on('message', function () {
+ isCalled = true;
+ });
+
+ publisher.publish('testChannel', 'testMessage');
+ // Make sure the callback is not called
+ expect(isCalled).toBe(false);
+ });
+
+ 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/ExportAdapter.spec.js b/spec/ExportAdapter.spec.js
deleted file mode 100644
index 95fbdd2190..0000000000
--- a/spec/ExportAdapter.spec.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var ExportAdapter = require('../ExportAdapter');
-
-describe('ExportAdapter', () => {
- it('can be constructed', (done) => {
- var database = new ExportAdapter('mongodb://localhost:27017/test',
- {
- collectionPrefix: 'test_'
- });
- database.connect().then(done, (error) => {
- console.log('error', error.stack);
- fail();
- });
- });
-
-});
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/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
new file mode 100644
index 0000000000..30acf7d13c
--- /dev/null
+++ b/spec/FilesController.spec.js
@@ -0,0 +1,221 @@
+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 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();
+
+ 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 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);
+ }
+ 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/GCM.spec.js b/spec/GCM.spec.js
deleted file mode 100644
index d7484b0ea9..0000000000
--- a/spec/GCM.spec.js
+++ /dev/null
@@ -1,137 +0,0 @@
-var GCM = require('../GCM');
-
-describe('GCM', () => {
- it('can generate GCM Payload without expiration time', (done) => {
- //Mock request data
- var data = {
- 'alert': 'alert'
- };
- var pushId = 1;
- var timeStamp = 1454538822113;
- var timeStampISOStr = new Date(timeStamp).toISOString();
-
- var payload = GCM.generateGCMPayload(data, pushId, timeStamp);
-
- expect(payload.priority).toEqual('normal');
- expect(payload.timeToLive).toEqual(undefined);
- var dataFromPayload = payload.data;
- expect(dataFromPayload.time).toEqual(timeStampISOStr);
- expect(dataFromPayload['push_id']).toEqual(pushId);
- var dataFromUser = JSON.parse(dataFromPayload.data);
- expect(dataFromUser).toEqual(data);
- done();
- });
-
- it('can generate GCM Payload with valid expiration time', (done) => {
- //Mock request data
- var data = {
- 'alert': 'alert'
- };
- var pushId = 1;
- var timeStamp = 1454538822113;
- var timeStampISOStr = new Date(timeStamp).toISOString();
- var expirationTime = 1454538922113
-
- var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
-
- expect(payload.priority).toEqual('normal');
- expect(payload.timeToLive).toEqual(Math.floor((expirationTime - timeStamp) / 1000));
- var dataFromPayload = payload.data;
- expect(dataFromPayload.time).toEqual(timeStampISOStr);
- expect(dataFromPayload['push_id']).toEqual(pushId);
- var dataFromUser = JSON.parse(dataFromPayload.data);
- expect(dataFromUser).toEqual(data);
- done();
- });
-
- it('can generate GCM Payload with too early expiration time', (done) => {
- //Mock request data
- var data = {
- 'alert': 'alert'
- };
- var pushId = 1;
- var timeStamp = 1454538822113;
- var timeStampISOStr = new Date(timeStamp).toISOString();
- var expirationTime = 1454538822112;
-
- var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
-
- expect(payload.priority).toEqual('normal');
- expect(payload.timeToLive).toEqual(0);
- var dataFromPayload = payload.data;
- expect(dataFromPayload.time).toEqual(timeStampISOStr);
- expect(dataFromPayload['push_id']).toEqual(pushId);
- var dataFromUser = JSON.parse(dataFromPayload.data);
- expect(dataFromUser).toEqual(data);
- done();
- });
-
- it('can generate GCM Payload with too late expiration time', (done) => {
- //Mock request data
- var data = {
- 'alert': 'alert'
- };
- var pushId = 1;
- var timeStamp = 1454538822113;
- var timeStampISOStr = new Date(timeStamp).toISOString();
- var expirationTime = 2454538822113;
-
- var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime);
-
- expect(payload.priority).toEqual('normal');
- // Four week in second
- expect(payload.timeToLive).toEqual(4 * 7 * 24 * 60 * 60);
- var dataFromPayload = payload.data;
- expect(dataFromPayload.time).toEqual(timeStampISOStr);
- expect(dataFromPayload['push_id']).toEqual(pushId);
- var dataFromUser = JSON.parse(dataFromPayload.data);
- expect(dataFromUser).toEqual(data);
- done();
- });
-
- it('can send GCM request', (done) => {
- var gcm = new GCM('apiKey');
- // Mock gcm sender
- var sender = {
- send: jasmine.createSpy('send')
- };
- gcm.sender = sender;
- // Mock data
- var expirationTime = 2454538822113;
- var data = {
- 'expiration_time': expirationTime,
- 'data': {
- 'alert': 'alert'
- }
- }
- // Mock registrationTokens
- var registrationTokens = ['token'];
-
- var promise = gcm.send(data, registrationTokens);
- expect(sender.send).toHaveBeenCalled();
- var args = sender.send.calls.first().args;
- // It is too hard to verify message of gcm library, we just verify tokens and retry times
- expect(args[1].registrationTokens).toEqual(registrationTokens);
- expect(args[2]).toEqual(5);
- done();
- });
-
- it('can throw on sending when we have too many registration tokens', (done) => {
- var gcm = new GCM('apiKey');
- // Mock gcm sender
- var sender = {
- send: jasmine.createSpy('send')
- };
- gcm.sender = sender;
- // Mock registrationTokens
- var registrationTokens = [];
- for (var i = 0; i <= 2000; i++) {
- registrationTokens.push(i.toString());
- }
-
- expect(function() {
- gcm.send({}, registrationTokens);
- }).toThrow();
- 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/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js
new file mode 100644
index 0000000000..b138a010b0
--- /dev/null
+++ b/spec/HTTPRequest.spec.js
@@ -0,0 +1,268 @@
+'use strict';
+
+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('/404', function (req, res) {
+ res.status(404);
+ res.send('NO');
+ });
+
+ app.get('/301', function (req, res) {
+ res.status(301);
+ res.location('/hello');
+ res.send();
+ });
+
+ app.post('/echo', function (req, res) {
+ res.json(req.body);
+ });
+
+ app.get('/qs', function (req, res) {
+ res.json(req.query);
+ });
+
+ return app.listen(13371, undefined, done);
+}
+
+describe('httpRequest', () => {
+ let server;
+ beforeEach(done => {
+ if (!server) {
+ server = startServer(done);
+ } else {
+ done();
+ }
+ });
+
+ afterAll(done => {
+ server.close(done);
+ });
+
+ it('should do /hello', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/hello`,
+ });
+
+ 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 do not follow redirects by default', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/301`,
+ });
+
+ expect(httpResponse.status).toBe(301);
+ });
+
+ it('should follow redirects when set', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/301`,
+ followRedirects: true,
+ });
+
+ 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',
+ },
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.data).toEqual({ foo: 'bar' });
+ });
+
+ 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');
+ });
+
+ it('should encode a JSON body', () => {
+ const options = {
+ body: { foo: 'bar' },
+ headers: { 'Content-Type': 'application/json' },
+ };
+ const result = httpRequest.encodeBody(options);
+
+ expect(result.body).toEqual('{"foo":"bar"}');
+ });
+
+ 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', () => {
+ 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', async () => {
+ await expectAsync(
+ httpRequest({
+ url: 'http://not a good url',
+ })
+ ).toBeRejected();
+ });
+
+ it('should params object to query string', async () => {
+ const httpResponse = await httpRequest({
+ url: `${httpRequestServer}/qs`,
+ params: {
+ foo: 'bar',
+ },
+ });
+
+ expect(httpResponse.status).toBe(200);
+ expect(httpResponse.data).toEqual({ foo: 'bar' });
+ });
+
+ 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', () => {
+ const httpResponse = new HTTPResponse({});
+ expect(httpResponse.body).toBeUndefined();
+ expect(httpResponse.data).toBeUndefined();
+ expect(httpResponse.text).toBeUndefined();
+ expect(httpResponse.buffer).toBeUndefined();
+ });
+
+ it('serialized httpResponse correctly with body string', () => {
+ const httpResponse = new HTTPResponse({}, 'hello');
+ expect(httpResponse.text).toBe('hello');
+ expect(httpResponse.data).toBe(undefined);
+ expect(httpResponse.body).toBe('hello');
+
+ 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', () => {
+ 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(result.text).toEqual('{"foo":"bar"}');
+ expect(result.data).toEqual({ foo: 'bar' });
+ expect(result.body).toEqual(undefined);
+ });
+
+ it('serialized httpResponse correctly with body buffer string', () => {
+ const httpResponse = new HTTPResponse({}, Buffer.from('hello'));
+ expect(httpResponse.text).toBe('hello');
+ expect(httpResponse.data).toBe(undefined);
+
+ 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', () => {
+ 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' });
+ });
+
+ it('serialized httpResponse with Parse._encode should be allright', () => {
+ 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') {
+ foundText = true;
+ }
+ 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
new file mode 100644
index 0000000000..4a474b7fa2
--- /dev/null
+++ b/spec/InMemoryCache.spec.js
@@ -0,0 +1,71 @@
+const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default;
+
+describe('InMemoryCache', function () {
+ const BASE_TTL = {
+ ttl: 100,
+ };
+ const NO_EXPIRE_TTL = {
+ ttl: NaN,
+ };
+ const KEY = 'hello';
+ const KEY_2 = KEY + '_2';
+
+ const VALUE = 'world';
+
+ function wait(sleep) {
+ return new Promise(function (resolve) {
+ setTimeout(resolve, sleep);
+ });
+ }
+
+ it('should destroy a expire items in the cache', done => {
+ const cache = new InMemoryCache(BASE_TTL);
+
+ cache.put(KEY, VALUE);
+
+ let value = cache.get(KEY);
+ expect(value).toEqual(VALUE);
+
+ wait(BASE_TTL.ttl * 10).then(() => {
+ value = cache.get(KEY);
+ expect(value).toEqual(null);
+ done();
+ });
+ });
+
+ 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);
+ expect(cache.get(KEY_2)).toEqual(VALUE);
+
+ cache.del(KEY);
+ expect(cache.get(KEY)).toEqual(null);
+ expect(cache.get(KEY_2)).toEqual(VALUE);
+
+ cache.del(KEY_2);
+ expect(cache.get(KEY)).toEqual(null);
+ expect(cache.get(KEY_2)).toEqual(null);
+ done();
+ });
+
+ it('should clear all items', done => {
+ const cache = new InMemoryCache(NO_EXPIRE_TTL);
+ cache.put(KEY, VALUE);
+ cache.put(KEY_2, VALUE);
+
+ expect(cache.get(KEY)).toEqual(VALUE);
+ expect(cache.get(KEY_2)).toEqual(VALUE);
+ cache.clear();
+
+ expect(cache.get(KEY)).toEqual(null);
+ expect(cache.get(KEY_2)).toEqual(null);
+ done();
+ });
+
+ it('should deafult TTL to 5 seconds', () => {
+ const cache = new InMemoryCache({});
+ expect(cache.ttl).toEqual(5 * 1000);
+ });
+});
diff --git a/spec/InMemoryCacheAdapter.spec.js b/spec/InMemoryCacheAdapter.spec.js
new file mode 100644
index 0000000000..add976fbc9
--- /dev/null
+++ b/spec/InMemoryCacheAdapter.spec.js
@@ -0,0 +1,53 @@
+const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').default;
+
+describe('InMemoryCacheAdapter', function () {
+ const KEY = 'hello';
+ const VALUE = 'world';
+
+ function wait(sleep) {
+ return new Promise(function (resolve) {
+ setTimeout(resolve, sleep);
+ });
+ }
+
+ it('should expose promisifyed methods', done => {
+ const cache = new InMemoryCacheAdapter({
+ 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 InMemoryCacheAdapter({
+ ttl: NaN,
+ });
+
+ cache
+ .put(KEY, VALUE)
+ .then(() => cache.get(KEY))
+ .then(value => expect(value).toEqual(VALUE))
+ .then(() => cache.clear())
+ .then(() => cache.get(KEY))
+ .then(value => expect(value).toEqual(null))
+ .then(done);
+ });
+
+ it('should expire after ttl', done => {
+ const cache = new InMemoryCacheAdapter({
+ ttl: 10,
+ });
+
+ cache
+ .put(KEY, VALUE)
+ .then(() => cache.get(KEY))
+ .then(value => expect(value).toEqual(VALUE))
+ .then(wait.bind(null, 50))
+ .then(() => cache.get(KEY))
+ .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
new file mode 100644
index 0000000000..1ccad5013d
--- /dev/null
+++ b/spec/InstallationsRouter.spec.js
@@ -0,0 +1,247 @@
+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 => {
+ 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: {
+ where: {
+ deviceType: 'android',
+ },
+ },
+ query: {},
+ info: {},
+ };
+
+ 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 => {
+ 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: {
+ where: {
+ deviceType: 'android',
+ },
+ },
+ info: {},
+ };
+
+ 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 => {
+ 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: {
+ limit: 0,
+ },
+ info: {},
+ };
+
+ 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_exclude_dbs(['postgres'])('query installations with count = 1', done => {
+ 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();
+ 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');
+
+ res = await router.handleFind(request);
+ response = res.response;
+ expect(response.results.length).toEqual(2);
+ expect(response.count).toEqual(2);
+ });
+
+ 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',
+ };
+ const iosDeviceRequest = {
+ installationId: '12345678-abcd-abcd-abcd-123456789abd',
+ deviceType: 'ios',
+ };
+ const request = {
+ config: config,
+ auth: auth.master(config),
+ body: {},
+ query: {
+ limit: 0,
+ count: 1,
+ },
+ info: {},
+ };
+
+ 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
new file mode 100644
index 0000000000..37d477444c
--- /dev/null
+++ b/spec/LoggerController.spec.js
@@ -0,0 +1,166 @@
+const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
+const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
+ .WinstonLoggerAdapter;
+
+describe('LoggerController', () => {
+ it('can process an empty query without throwing', done => {
+ // Make mock request
+ const query = {};
+
+ const loggerController = new LoggerController(new WinstonLoggerAdapter());
+
+ expect(() => {
+ 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 => {
+ expect(LoggerController.validDateTime()).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);
+ done();
+ });
+
+ it('can set the proper default values', done => {
+ // Make mock request
+ const result = LoggerController.parseOptions();
+ expect(result.size).toEqual(10);
+ expect(result.order).toEqual('desc');
+ expect(result.level).toEqual('info');
+
+ done();
+ });
+
+ it('can parse an ascending query without throwing', done => {
+ // Make mock request
+ const query = {
+ from: '2016-01-01Z00:00:00',
+ until: '2016-01-01Z00:00:00',
+ size: 5,
+ order: 'asc',
+ level: 'error',
+ };
+
+ 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('asc');
+ expect(result.level).toEqual('error');
+
+ 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
+ const query = {
+ from: '2016-01-01Z00:00:00',
+ until: '2016-01-01Z00:00:00',
+ size: 5,
+ order: 'desc',
+ level: 'error',
+ };
+
+ 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).not.toBe(0);
+ done();
+ })
+ .catch(err => {
+ jfail(err);
+ fail('should not fail');
+ done();
+ });
+ }).not.toThrow();
+ });
+
+ it('should throw without an adapter', done => {
+ expect(() => {
+ 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
new file mode 100644
index 0000000000..d4b77baaa8
--- /dev/null
+++ b/spec/LogsRouter.spec.js
@@ -0,0 +1,177 @@
+'use strict';
+
+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 WinstonLoggerAdapter());
+
+describe_only(() => {
+ return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug';
+})('LogsRouter', () => {
+ it('can check valid master key of request', done => {
+ // Make mock request
+ const request = {
+ auth: {
+ isMaster: true,
+ },
+ query: {},
+ config: {
+ loggerController: loggerController,
+ },
+ };
+
+ const router = new LogsRouter();
+
+ expect(() => {
+ router.validateRequest(request);
+ }).not.toThrow();
+ done();
+ });
+
+ it('can check invalid construction of controller', done => {
+ // Make mock request
+ const request = {
+ auth: {
+ isMaster: true,
+ },
+ query: {},
+ config: {
+ loggerController: undefined, // missing controller
+ },
+ };
+
+ const router = new LogsRouter();
+
+ expect(() => {
+ router.validateRequest(request);
+ }).toThrow();
+ done();
+ });
+
+ it('can check invalid master key of request', done => {
+ const logger = require('../lib/logger').default;
+ const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
+ loggerErrorSpy.calls.reset();
+ request({
+ url: 'http://localhost:8378/1/scriptlog',
+ headers: {
+ 'X-Parse-Application-Id': 'test',
+ '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
new file mode 100644
index 0000000000..d05a56970b
--- /dev/null
+++ b/spec/Middlewares.spec.js
@@ -0,0 +1,593 @@
+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, {});
+ });
+
+ afterEach(() => {
+ AppCache.del(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();
+ });
+ });
+
+ 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();
+ });
+ });
+
+ 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);
+ });
+
+ 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'
+ );
+ });
+
+ 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);
+
+ AppCachePut(fakeReq.body._ApplicationId, {
+ allowHeaders: [],
+ });
+ allowCrossDomain(fakeReq, res, () => {});
+ expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS);
+ });
+
+ 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);
+ });
+
+ 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('*');
+ });
+
+ 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/');
+ });
+
+ 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);
+ });
+
+ 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/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js
new file mode 100644
index 0000000000..8e376b9d1d
--- /dev/null
+++ b/spec/MongoSchemaCollectionAdapter.spec.js
@@ -0,0 +1,99 @@
+'use strict';
+
+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,
+ 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' },
+ deviceToken: { type: 'String' },
+ deviceType: { type: 'String' },
+ channels: { type: 'Array' },
+ user: { type: 'Pointer', targetClass: '_User' },
+ 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' },
+ ACL: { type: 'ACL' },
+ createdAt: { type: 'Date' },
+ updatedAt: { type: 'Date' },
+ 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
new file mode 100644
index 0000000000..facf1b2c41
--- /dev/null
+++ b/spec/MongoStorageAdapter.spec.js
@@ -0,0 +1,1291 @@
+'use strict';
+
+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_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(fakeClient));
+ new MongoStorageAdapter({
+ 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',
+ jasmine.any(Object)
+ );
+ });
+
+ it("doesn't double escape already URI-encoded information", () => {
+ 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',
+ }).connect();
+ expect(MongoClient.connect).toHaveBeenCalledWith(
+ 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
+ jasmine.any(Object)
+ );
+ });
+
+ // https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057
+ it('preserves replica sets', () => {
+ 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',
+ }).connect();
+ expect(MongoClient.connect).toHaveBeenCalledWith(
+ 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',
+ jasmine.any(Object)
+ );
+ });
+
+ it('stores objectId in _id', 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('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',
+ },
+ };
+ 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 => {
+ 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' },
+ date: {
+ __type: 'Date',
+ iso: '2016-05-26T20:55:01.154Z',
+ },
+ };
+ 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
new file mode 100644
index 0000000000..a3c61214a8
--- /dev/null
+++ b/spec/MongoTransform.spec.js
@@ -0,0 +1,696 @@
+// These tests are unit tests designed to only test transform.js.
+'use strict';
+
+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 => {
+ 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 => {
+ 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 with date', done => {
+ const input = {
+ createdAt: '2015-10-06T21:24:50.332Z',
+ updatedAt: '2015-10-06T21:24:50.332Z',
+ };
+ 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 => {
+ const pointer = {
+ __type: 'Pointer',
+ objectId: 'myId',
+ className: 'Blah',
+ };
+ 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 => {
+ 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 => {
+ const input = { ACL: { '0123': { read: true, write: true } } };
+ expect(() =>
+ transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} })
+ ).toThrow();
+ done();
+ });
+
+ 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('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 => {
+ 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 => {
+ const out = transform.transformWhere(null, { objectId: 'foo' });
+ expect(out._id).toEqual('foo');
+ done();
+ });
+
+ it('objectId in a list', done => {
+ const input = {
+ objectId: { $in: ['one', 'two', 'three'] },
+ };
+ const output = transform.transformWhere(null, input);
+ jequal(input.objectId, output._id);
+ done();
+ });
+
+ 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 => {
+ 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',
+ });
+ done();
+ });
+
+ 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 => {
+ 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' });
+ done();
+ });
+
+ 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',
+ latitude: lat,
+ longitude: lng,
+ });
+ done();
+ });
+
+ 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' }]);
+ done();
+ });
+
+ it('untransforms objects containing nested special keys', done => {
+ 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 => {
+ const input = {
+ somePointer: { __type: 'Pointer', className: 'Micro', objectId: 'oft' },
+ };
+ 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 => {
+ const input = {
+ userPointer: {
+ __type: 'Pointer',
+ className: '_User',
+ objectId: 'qwerty',
+ },
+ };
+ 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 on create', done => {
+ const input = {
+ _rperm: ['*'],
+ _wperm: ['Kevin'],
+ };
+
+ 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._rperm).toEqual(input._rperm);
+ expect(output._wperm).toEqual(input._wperm);
+ done();
+ });
+
+ it('removes Relation types', done => {
+ const input = {
+ aRelation: { __type: 'Relation', className: 'Stuff' },
+ };
+ 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();
+ done();
+ });
+
+ it('untransforms mongodb number types', done => {
+ const input = {
+ long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
+ double: new mongodb.Double(Number.MAX_VALUE),
+ };
+ const output = transform.mongoObjectToParseObject(null, input, {
+ fields: {
+ long: { type: 'Number' },
+ double: { type: 'Number' },
+ },
+ });
+ expect(output.long).toBe(Number.MAX_SAFE_INTEGER);
+ expect(output.double).toBe(Number.MAX_VALUE);
+ 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/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 = '">