diff --git a/README.md b/README.md
index de0b21d442..e0a10a79d2 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ A big _thank you_ 🙏 to our [sponsors](#sponsors) and [backers](#backers) who
- [Using Environment Variables](#using-environment-variables)
- [Available Adapters](#available-adapters)
- [Configuring File Adapters](#configuring-file-adapters)
+ - [Restricting File URL Domains](#restricting-file-url-domains)
- [Idempotency Enforcement](#idempotency-enforcement)
- [Localization](#localization)
- [Pages](#pages)
@@ -491,6 +492,33 @@ Parse Server allows developers to choose from several options when hosting files
`GridFSBucketAdapter` is used by default and requires no setup, but if you're interested in using Amazon S3, Google Cloud Storage, or local file storage, additional configuration information is available in the [Parse Server guide](http://docs.parseplatform.org/parse-server/guide/#configuring-file-adapters).
+### Restricting File URL Domains
+
+Parse objects can reference files by URL. To prevent [SSRF attacks](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery) via crafted file URLs, you can restrict the allowed URL domains using the `fileUpload.allowedFileUrlDomains` option.
+
+This protects against scenarios where an attacker provides a `Parse.File` with an arbitrary URL, for example as a Cloud Function parameter or in a field of type `Object` or `Array`. If Cloud Code or a client calls `getData()` on such a file, the Parse SDK makes an HTTP request to that URL, potentially leaking the server or client IP address and accessing internal services.
+
+> [!NOTE]
+> Fields of type `Parse.File` in the Parse schema are not affected by this attack, because Parse Server discards the URL on write and dynamically generates it on read based on the file adapter configuration.
+
+```javascript
+const parseServer = new ParseServer({
+ ...otherOptions,
+ fileUpload: {
+ allowedFileUrlDomains: ['cdn.example.com', '*.example.com'],
+ },
+});
+```
+
+| Parameter | Optional | Type | Default | Environment Variable |
+|---|---|---|---|---|
+| `fileUpload.allowedFileUrlDomains` | yes | `String[]` | `['*']` | `PARSE_SERVER_FILE_UPLOAD_ALLOWED_FILE_URL_DOMAINS` |
+
+- `['*']` (default) allows file URLs with any domain.
+- `['cdn.example.com']` allows only exact hostname matches.
+- `['*.example.com']` allows any subdomain of `example.com`.
+- `[]` blocks all file URLs; only files referenced by name are allowed.
+
## Idempotency Enforcement
**Caution, this is an experimental feature that may not be appropriate for production.**
diff --git a/benchmark/performance.js b/benchmark/performance.js
index c37ac09777..b373a6a7a8 100644
--- a/benchmark/performance.js
+++ b/benchmark/performance.js
@@ -8,7 +8,6 @@
* Run with: npm run benchmark
*/
-const core = require('@actions/core');
const Parse = require('parse/node');
const { performance } = require('node:perf_hooks');
const { MongoClient } = require('mongodb');
@@ -25,6 +24,7 @@ const LOG_ITERATIONS = false;
// Parse Server instance
let parseServer;
let mongoClient;
+let core;
// Logging helpers
const logInfo = message => core.info(message);
@@ -529,6 +529,7 @@ async function benchmarkQueryWithIncludeNested(name) {
* Run all benchmarks
*/
async function runBenchmarks() {
+ core = await import('@actions/core');
logInfo('Starting Parse Server Performance Benchmarks...');
let server;
diff --git a/changelogs/CHANGELOG_alpha.md b/changelogs/CHANGELOG_alpha.md
index 89be5e840a..1ea16fd25b 100644
--- a/changelogs/CHANGELOG_alpha.md
+++ b/changelogs/CHANGELOG_alpha.md
@@ -1,3 +1,80 @@
+# [9.3.0-alpha.9](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.8...9.3.0-alpha.9) (2026-02-21)
+
+
+### Features
+
+* Add support for streaming file upload via `Buffer`, `Readable`, `ReadableStream` ([#10065](https://github.com/parse-community/parse-server/issues/10065)) ([f0feb48](https://github.com/parse-community/parse-server/commit/f0feb48d0fb697a161693721eadd09d740336283))
+
+# [9.3.0-alpha.8](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.7...9.3.0-alpha.8) (2026-02-21)
+
+
+### Bug Fixes
+
+* Incorrect dependency chain of `Parse` uses browser build instead of Node build ([#10067](https://github.com/parse-community/parse-server/issues/10067)) ([1a2521d](https://github.com/parse-community/parse-server/commit/1a2521d930b855845aa13fde700b2e8170ff65a1))
+
+# [9.3.0-alpha.7](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.6...9.3.0-alpha.7) (2026-02-20)
+
+
+### Features
+
+* Upgrade to parse 8.2.0, @parse/push-adapter 8.3.0 ([#10066](https://github.com/parse-community/parse-server/issues/10066)) ([8b5a14e](https://github.com/parse-community/parse-server/commit/8b5a14ecaf0b58b899651fb97d43e0e5d9be506d))
+
+# [9.3.0-alpha.6](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.5...9.3.0-alpha.6) (2026-02-14)
+
+
+### Bug Fixes
+
+* Default ACL overwrites custom ACL on `Parse.Object` update ([#10061](https://github.com/parse-community/parse-server/issues/10061)) ([4ef89d9](https://github.com/parse-community/parse-server/commit/4ef89d912c08bb24500a4d4142a3220f024a2d34))
+
+# [9.3.0-alpha.5](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.4...9.3.0-alpha.5) (2026-02-12)
+
+
+### Bug Fixes
+
+* `Parse.Query.select('authData')` for `_User` class doesn't return auth data ([#10055](https://github.com/parse-community/parse-server/issues/10055)) ([44a5bb1](https://github.com/parse-community/parse-server/commit/44a5bb105e11e6918e899e0f1427b0adb38d6d67))
+
+# [9.3.0-alpha.4](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.3...9.3.0-alpha.4) (2026-02-12)
+
+
+### Bug Fixes
+
+* Unlinking auth provider triggers auth data validation ([#10045](https://github.com/parse-community/parse-server/issues/10045)) ([b6b6327](https://github.com/parse-community/parse-server/commit/b6b632755263417c2a3c3a31381eedc516723740))
+
+# [9.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.2...9.3.0-alpha.3) (2026-02-07)
+
+
+### Features
+
+* Add `Parse.File.url` validation with config `fileUpload.allowedFileUrlDomains` against SSRF attacks ([#10044](https://github.com/parse-community/parse-server/issues/10044)) ([4c9c948](https://github.com/parse-community/parse-server/commit/4c9c9489f062bec6d751b23f4a68aea2a63936bd))
+
+# [9.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/9.3.0-alpha.1...9.3.0-alpha.2) (2026-02-06)
+
+
+### Bug Fixes
+
+* Default HTML pages for password reset, email verification not found ([#10041](https://github.com/parse-community/parse-server/issues/10041)) ([a4265bb](https://github.com/parse-community/parse-server/commit/a4265bb1241551b7147e8aee08c36e1f8ab09ba4))
+
+# [9.3.0-alpha.1](https://github.com/parse-community/parse-server/compare/9.2.1-alpha.2...9.3.0-alpha.1) (2026-02-06)
+
+
+### Features
+
+* Add event information to `verifyUserEmails`, `preventLoginWithUnverifiedEmail` to identify invoking signup / login action and auth provider ([#9963](https://github.com/parse-community/parse-server/issues/9963)) ([ed98c15](https://github.com/parse-community/parse-server/commit/ed98c15f90f2fa6a66780941fd3705b805d6eb14))
+
+## [9.2.1-alpha.2](https://github.com/parse-community/parse-server/compare/9.2.1-alpha.1...9.2.1-alpha.2) (2026-02-06)
+
+
+### Bug Fixes
+
+* AuthData validation incorrectly triggered on unchanged providers ([#10025](https://github.com/parse-community/parse-server/issues/10025)) ([d3d6e9e](https://github.com/parse-community/parse-server/commit/d3d6e9e22a212885690853cbbb84bb8c53da5646))
+
+## [9.2.1-alpha.1](https://github.com/parse-community/parse-server/compare/9.2.0...9.2.1-alpha.1) (2026-02-06)
+
+
+### Bug Fixes
+
+* Default HTML pages for password reset, email verification not found ([#10034](https://github.com/parse-community/parse-server/issues/10034)) ([e299107](https://github.com/parse-community/parse-server/commit/e29910764daef3c03ed1b09eee19cedc3b12a86a))
+
# [9.2.0-alpha.5](https://github.com/parse-community/parse-server/compare/9.2.0-alpha.4...9.2.0-alpha.5) (2026-02-05)
diff --git a/ci/CiVersionCheck.js b/ci/CiVersionCheck.js
index b06620b246..20986a0b15 100644
--- a/ci/CiVersionCheck.js
+++ b/ci/CiVersionCheck.js
@@ -1,4 +1,3 @@
-const core = require('@actions/core');
const semver = require('semver');
const yaml = require('yaml');
const fs = require('fs').promises;
@@ -220,6 +219,7 @@ class CiVersionCheck {
* 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...`);
diff --git a/ci/definitionsCheck.js b/ci/definitionsCheck.js
index 476dad8d0e..b4b9e88d0a 100644
--- a/ci/definitionsCheck.js
+++ b/ci/definitionsCheck.js
@@ -1,8 +1,8 @@
const fs = require('fs').promises;
const { exec } = require('child_process');
-const core = require('@actions/core');
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'),
diff --git a/ci/nodeEngineCheck.js b/ci/nodeEngineCheck.js
index 65a806f760..e2c4553604 100644
--- a/ci/nodeEngineCheck.js
+++ b/ci/nodeEngineCheck.js
@@ -1,7 +1,7 @@
-const core = require('@actions/core');
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
@@ -137,6 +137,7 @@ class NodeEngineCheck {
}
async function check() {
+ core = await import('@actions/core');
// Define paths
const nodeModulesPath = path.join(__dirname, '../node_modules');
const packageJsonPath = path.join(__dirname, '../package.json');
diff --git a/package-lock.json b/package-lock.json
index c46e23133c..f075ff8510 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "parse-server",
- "version": "9.2.0",
+ "version": "9.3.0-alpha.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "parse-server",
- "version": "9.2.0",
+ "version": "9.3.0-alpha.9",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -16,13 +16,13 @@
"@graphql-tools/schema": "10.0.23",
"@graphql-tools/utils": "10.8.6",
"@parse/fs-files-adapter": "3.0.0",
- "@parse/push-adapter": "8.2.0",
+ "@parse/push-adapter": "8.3.1",
"bcryptjs": "3.0.3",
- "commander": "14.0.2",
+ "commander": "14.0.3",
"cors": "2.8.6",
"deepcopy": "2.1.0",
"express": "5.2.1",
- "express-rate-limit": "7.5.1",
+ "express-rate-limit": "8.2.1",
"follow-redirects": "1.15.9",
"graphql": "16.11.0",
"graphql-list-fields": "2.0.4",
@@ -38,9 +38,9 @@
"mongodb": "7.0.0",
"mustache": "4.2.0",
"otpauth": "9.4.0",
- "parse": "8.0.3",
+ "parse": "8.2.0",
"path-to-regexp": "8.3.0",
- "pg-monitor": "3.0.0",
+ "pg-monitor": "3.1.0",
"pg-promise": "12.6.0",
"pluralize": "8.0.0",
"punycode": "2.3.1",
@@ -58,13 +58,13 @@
"parse-server": "bin/parse-server"
},
"devDependencies": {
- "@actions/core": "1.11.1",
+ "@actions/core": "3.0.0",
"@apollo/client": "3.13.8",
"@babel/cli": "7.27.0",
- "@babel/core": "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.26.5",
+ "@babel/plugin-transform-flow-strip-types": "7.27.1",
"@babel/preset-env": "7.27.2",
"@babel/preset-typescript": "7.27.1",
"@saithodev/semantic-release-backmerge": "4.0.1",
@@ -73,7 +73,7 @@
"@semantic-release/git": "10.0.1",
"@semantic-release/github": "11.0.3",
"@semantic-release/npm": "12.0.1",
- "@semantic-release/release-notes-generator": "14.0.3",
+ "@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",
@@ -81,9 +81,9 @@
"deep-diff": "1.0.2",
"eslint": "9.27.0",
"eslint-plugin-expect-type": "0.6.2",
- "eslint-plugin-unused-imports": "4.3.0",
+ "eslint-plugin-unused-imports": "4.4.1",
"form-data": "4.0.5",
- "globals": "16.2.0",
+ "globals": "17.3.0",
"graphql-tag": "2.12.6",
"jasmine": "5.7.1",
"jasmine-spec-reporter": "7.0.0",
@@ -98,11 +98,11 @@
"node-abort-controller": "3.1.1",
"node-fetch": "3.2.10",
"nyc": "17.1.0",
- "prettier": "2.0.5",
+ "prettier": "3.8.1",
"semantic-release": "24.2.5",
- "typescript": "5.8.3",
+ "typescript": "5.9.3",
"typescript-eslint": "8.53.1",
- "yaml": "2.8.0"
+ "yaml": "2.8.2"
},
"engines": {
"node": ">=20.19.0 <21.0.0 || >=22.12.0 <23.0.0 || >=24.11.0 <25.0.0"
@@ -116,37 +116,38 @@
}
},
"node_modules/@actions/core": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
- "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
+ "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": "^1.1.1",
- "@actions/http-client": "^2.0.1"
+ "@actions/exec": "^3.0.0",
+ "@actions/http-client": "^4.0.0"
}
},
"node_modules/@actions/exec": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
- "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
+ "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": "^1.0.1"
+ "@actions/io": "^3.0.2"
}
},
"node_modules/@actions/http-client": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
- "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
+ "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"
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
}
},
"node_modules/@actions/io": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
- "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
+ "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": {
@@ -497,9 +498,9 @@
"dev": true
},
"node_modules/@babel/code-frame": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
- "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "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",
@@ -520,20 +521,20 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
- "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
+ "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.28.6",
- "@babel/generator": "^7.28.6",
+ "@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.28.6",
+ "@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^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",
@@ -592,13 +593,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz",
- "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
+ "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.28.6",
- "@babel/types": "^7.28.6",
+ "@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"
@@ -802,11 +803,10 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "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,
- "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -919,12 +919,12 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
- "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+ "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.28.6"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -1050,13 +1050,12 @@
}
},
"node_modules/@babel/plugin-syntax-flow": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
- "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
+ "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,
- "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
+ "@babel/helper-plugin-utils": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -1438,14 +1437,13 @@
}
},
"node_modules/@babel/plugin-transform-flow-strip-types": {
- "version": "7.26.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz",
- "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==",
+ "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,
- "license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.26.5",
- "@babel/plugin-syntax-flow": "^7.26.0"
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-flow": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -2177,6 +2175,18 @@
"node": ">=6.9.0"
}
},
+ "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/@babel/template": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
@@ -2192,17 +2202,17 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz",
- "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
+ "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.28.6",
- "@babel/generator": "^7.28.6",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.6",
+ "@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
- "@babel/types": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
@@ -2210,9 +2220,9 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
- "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+ "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",
@@ -2604,9 +2614,9 @@
}
},
"node_modules/@google-cloud/storage": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.18.0.tgz",
- "integrity": "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==",
+ "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": {
@@ -2616,7 +2626,7 @@
"abort-controller": "^3.0.0",
"async-retry": "^1.3.3",
"duplexify": "^4.1.3",
- "fast-xml-parser": "^4.4.1",
+ "fast-xml-parser": "^5.3.4",
"gaxios": "^6.0.2",
"google-auth-library": "^9.6.3",
"html-entities": "^2.5.2",
@@ -2714,9 +2724,9 @@
}
},
"node_modules/@grpc/grpc-js": {
- "version": "1.14.2",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.2.tgz",
- "integrity": "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA==",
+ "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": {
@@ -3044,7 +3054,7 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
@@ -3061,7 +3071,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=12"
},
@@ -3073,7 +3083,7 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=12"
},
@@ -3085,13 +3095,13 @@
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true
+ "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==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
@@ -3108,7 +3118,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -3123,7 +3133,7 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
@@ -4087,13 +4097,13 @@
"integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
},
"node_modules/@parse/node-apn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.0.1.tgz",
- "integrity": "sha512-2xBiaznvupLOoXFaxWxcWcqCGlRn9rvqeAQnv8ogL8hZPe1Rd0es+F8ppE7g4QIy5DPJv0R4fruB8amGM6K/qA==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.1.0.tgz",
+ "integrity": "sha512-a40P5nScLDi9Pf7koKKkbwI73px0q+iLaKYNrr7kyKJebq/4duGOy3mMevZS0zltn171k3jB5BWCC27dPGsMmw==",
"license": "MIT",
"dependencies": {
"debug": "4.4.3",
- "jsonwebtoken": "9.0.2",
+ "jsonwebtoken": "9.0.3",
"node-forge": "1.3.2",
"verror": "1.10.1"
},
@@ -4101,17 +4111,60 @@
"node": ">=18"
}
},
+ "node_modules/@parse/node-apn/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/@parse/node-apn/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/@parse/node-apn/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/@parse/push-adapter": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.2.0.tgz",
- "integrity": "sha512-z5RB1TwNELNSvummTVP1fgncOT424j13HeKxsGHpAftUmjE+hUtSsIeG49chD0gac22Zmrk+7flYjRwQpUs6+w==",
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.3.1.tgz",
+ "integrity": "sha512-UOkL0bTOtUx4R866XtBwGGYvkNLQrQPrPWC4uzpbd9vR8tbfYIQQvBDT7eg58GryLb4EG+NOP8enm/VkhbwMVw==",
"license": "MIT",
"dependencies": {
- "@parse/node-apn": "7.0.1",
- "expo-server-sdk": "4.0.0",
- "firebase-admin": "13.6.0",
+ "@parse/node-apn": "7.1.0",
+ "expo-server-sdk": "5.0.0",
+ "firebase-admin": "13.6.1",
"npmlog": "7.0.1",
- "parse": "8.0.3",
+ "parse": "8.2.0",
"web-push": "3.6.7"
},
"engines": {
@@ -4649,18 +4702,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@saithodev/semantic-release-backmerge/node_modules/agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "dependencies": {
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"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",
@@ -4945,19 +4986,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@saithodev/semantic-release-backmerge/node_modules/https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "dev": true,
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"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",
@@ -5445,18 +5473,6 @@
"node": ">=18"
}
},
- "node_modules/@semantic-release/github/node_modules/agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "dependencies": {
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"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",
@@ -5520,19 +5536,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@semantic-release/github/node_modules/https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "dev": true,
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"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",
@@ -5814,9 +5817,9 @@
}
},
"node_modules/@semantic-release/release-notes-generator": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz",
- "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==",
+ "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",
@@ -7151,15 +7154,12 @@
}
},
"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==",
- "optional": true,
- "dependencies": {
- "debug": "4"
- },
+ "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": ">= 6.0.0"
+ "node": ">= 14"
}
},
"node_modules/aggregate-error": {
@@ -7531,7 +7531,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "devOptional": true
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -7568,9 +7568,10 @@
"license": "Apache-2.0"
},
"node_modules/bignumber.js": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
- "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
+ "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": "*"
}
@@ -8555,9 +8556,9 @@
}
},
"node_modules/commander": {
- "version": "14.0.2",
- "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
- "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==",
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"engines": {
"node": ">=20"
}
@@ -8722,9 +8723,9 @@
}
},
"node_modules/core-js-pure": {
- "version": "3.46.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.46.0.tgz",
- "integrity": "sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==",
+ "version": "3.48.0",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.48.0.tgz",
+ "integrity": "sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
@@ -8814,7 +8815,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -8860,7 +8861,7 @@
"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==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">= 12"
}
@@ -9470,7 +9471,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true
+ "devOptional": true
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
@@ -9916,14 +9917,13 @@
}
},
"node_modules/eslint-plugin-unused-imports": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz",
- "integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==",
+ "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,
- "license": "MIT",
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
- "eslint": "^9.0.0 || ^8.0.0"
+ "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@typescript-eslint/eslint-plugin": {
@@ -10244,59 +10244,26 @@
}
},
"node_modules/expo-server-sdk": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-4.0.0.tgz",
- "integrity": "sha512-zi83XtG2pqyP3gyn1JIRYkydo2i6HU3CYaWo/VvhZG/F29U+QIDv6LBEUsWf4ddZlVE7c9WN1N8Be49rHgO8OQ==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-5.0.0.tgz",
+ "integrity": "sha512-GEp1XYLU80iS/hdRo3c2n092E8TgTXcHSuw6Lw68dSoWaAgiLPI2R+e5hp5+hGF1TtJZOi2nxtJX63+XA3iz9g==",
"license": "MIT",
"dependencies": {
- "node-fetch": "^2.6.0",
"promise-limit": "^2.7.0",
- "promise-retry": "^2.0.1"
+ "promise-retry": "^2.0.1",
+ "undici": "^7.2.0"
},
"engines": {
"node": ">=20"
}
},
- "node_modules/expo-server-sdk/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==",
+ "node_modules/expo-server-sdk/node_modules/undici": {
+ "version": "7.22.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
+ "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
"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/expo-server-sdk/node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
- },
- "node_modules/expo-server-sdk/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"
- },
- "node_modules/expo-server-sdk/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",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
+ "node": ">=20.18.1"
}
},
"node_modules/express": {
@@ -10342,10 +10309,12 @@
}
},
"node_modules/express-rate-limit": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
- "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
- "license": "MIT",
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
+ "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==",
+ "dependencies": {
+ "ip-address": "10.0.1"
+ },
"engines": {
"node": ">= 16"
},
@@ -10378,7 +10347,8 @@
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
},
"node_modules/extract-files": {
"version": "13.0.0",
@@ -10463,9 +10433,9 @@
"dev": true
},
"node_modules/fast-xml-parser": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
- "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.7.tgz",
+ "integrity": "sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA==",
"funding": [
{
"type": "github",
@@ -10475,7 +10445,7 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "strnum": "^1.1.1"
+ "strnum": "^2.1.2"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -10521,7 +10491,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "github",
@@ -10844,9 +10814,9 @@
}
},
"node_modules/firebase-admin": {
- "version": "13.6.0",
- "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.0.tgz",
- "integrity": "sha512-GdPA/t0+Cq8p1JnjFRBmxRxAGvF/kl2yfdhALl38PrRp325YxyQ5aNaHui0XmaKcKiGRFIJ/EgBNWFoDP0onjw==",
+ "version": "13.6.1",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.1.tgz",
+ "integrity": "sha512-Zgc6yPtmPxAZo+FoK6LMG6zpSEsoSK8ifIR+IqF4oWuC3uWZU40OjxgfLTSFcsRlj/k/wD66zNv2UiTRreCNSw==",
"license": "Apache-2.0",
"dependencies": {
"@fastify/busboy": "^3.0.0",
@@ -10971,7 +10941,7 @@
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"fetch-blob": "^3.1.2"
},
@@ -11178,28 +11148,6 @@
"node": ">=14"
}
},
- "node_modules/gaxios/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/gaxios/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/gaxios/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -11255,6 +11203,170 @@
"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/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",
+ "optional": true,
+ "peer": true,
+ "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/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",
@@ -11463,11 +11575,10 @@
}
},
"node_modules/globals": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
- "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=18"
},
@@ -11550,6 +11661,15 @@
"node": ">=14"
}
},
+ "node_modules/google-auth-library/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",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/google-auth-library/node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
@@ -11565,6 +11685,7 @@
"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"
@@ -11655,10 +11776,12 @@
}
},
"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==",
+ "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",
+ "optional": true,
+ "peer": true,
"engines": {
"node": ">=14"
}
@@ -11807,6 +11930,7 @@
"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"
@@ -12065,18 +12189,6 @@
"node": ">= 14"
}
},
- "node_modules/http-proxy-agent/node_modules/agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "dependencies": {
- "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",
@@ -12092,16 +12204,16 @@
}
},
"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==",
- "optional": true,
+ "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": "6",
+ "agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
- "node": ">= 6"
+ "node": ">= 14"
}
},
"node_modules/human-signals": {
@@ -12274,6 +12386,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/ip-address": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
+ "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -12503,7 +12623,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "devOptional": true
},
"node_modules/issue-parser": {
"version": "7.0.1",
@@ -12691,7 +12811,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
@@ -12933,6 +13053,7 @@
"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"
}
@@ -14304,7 +14425,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -14543,6 +14664,21 @@
"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",
@@ -14599,6 +14735,56 @@
"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",
@@ -14645,6 +14831,60 @@
}
}
},
+ "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",
@@ -14813,7 +15053,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "github",
@@ -18098,7 +18338,7 @@
"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==",
- "dev": true
+ "devOptional": true
},
"node_modules/param-case": {
"version": "3.0.4",
@@ -18123,13 +18363,13 @@
}
},
"node_modules/parse": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/parse/-/parse-8.0.3.tgz",
- "integrity": "sha512-WQPrnfnXy6/p25OFD6qOAVK9hIhhU882Nw1AW5RjAJbO2G7YqChJxBgL94aexsaTnP9ajVzjGISSQ+mESrkMIA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.2.0.tgz",
+ "integrity": "sha512-jSx4zIqCja6O2HKhkzZ6JTm4fBUQS6sQpvFCAsqzaU4XlEhoRLm9mM1tZeYhvxTVA6zymvj/EJZ4YDOXCTRbmA==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "7.28.6",
- "@babel/runtime-corejs3": "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",
@@ -18166,18 +18406,6 @@
"node": ">=6"
}
},
- "node_modules/parse/node_modules/@babel/runtime-corejs3": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.6.tgz",
- "integrity": "sha512-kz2fAQ5UzjV7X7D3ySxmj3vRq89dTpqOZWv76Z6pNPztkwb/0Yj1Mtx1xFrYj6mbIHysxtBot8J4o0JLCblcFw==",
- "license": "MIT",
- "dependencies": {
- "core-js-pure": "^3.43.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/parse/node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
@@ -18263,7 +18491,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -18278,7 +18506,7 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@@ -18385,15 +18613,14 @@
}
},
"node_modules/pg-monitor": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-3.0.0.tgz",
- "integrity": "sha512-62jezmq3lR+lKCIsi9BXVg8Fxv+JG5LtaAuUmex5EVnBPlvAU7Ad6dOiQXHtH1xNh/Oy6Hux36k8uIjZWNeWtQ==",
- "license": "MIT",
+ "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"
+ "picocolors": "1.1.1"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
}
},
"node_modules/pg-pool": {
@@ -18772,15 +18999,19 @@
}
},
"node_modules/prettier": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
- "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
+ "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.js"
+ "prettier": "bin/prettier.cjs"
},
"engines": {
- "node": ">=10.13.0"
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-ms": {
@@ -18947,9 +19178,9 @@
}
},
"node_modules/qs": {
- "version": "6.14.1",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
- "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "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"
},
@@ -20197,7 +20428,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -20209,7 +20440,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -20641,7 +20872,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -20690,7 +20921,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -20739,9 +20970,9 @@
}
},
"node_modules/strnum": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
- "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
+ "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
"funding": [
{
"type": "github",
@@ -20988,6 +21219,19 @@
"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",
@@ -21003,6 +21247,20 @@
"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",
@@ -21509,11 +21767,10 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
- "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -21698,6 +21955,15 @@
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
+ "node_modules/undici": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
+ "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
+ "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",
@@ -21974,29 +22240,6 @@
"node": ">= 16"
}
},
- "node_modules/web-push/node_modules/agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dependencies": {
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/web-push/node_modules/https-proxy-agent": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
- "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
- "dependencies": {
- "agent-base": "^7.0.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/web-push/node_modules/jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
@@ -22020,7 +22263,7 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">= 8"
}
@@ -22081,7 +22324,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -22245,7 +22488,7 @@
"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,
+ "devOptional": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -22262,7 +22505,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -22277,7 +22520,7 @@
"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,
+ "devOptional": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -22289,7 +22532,7 @@
"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
+ "devOptional": true
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "4.3.0",
@@ -22389,16 +22632,18 @@
"dev": true
},
"node_modules/yaml": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true,
- "license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/yargs": {
@@ -22558,37 +22803,38 @@
},
"dependencies": {
"@actions/core": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
- "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
+ "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": "^1.1.1",
- "@actions/http-client": "^2.0.1"
+ "@actions/exec": "^3.0.0",
+ "@actions/http-client": "^4.0.0"
}
},
"@actions/exec": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
- "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
+ "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": "^1.0.1"
+ "@actions/io": "^3.0.2"
}
},
"@actions/http-client": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
- "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
+ "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"
+ "tunnel": "^0.0.6",
+ "undici": "^6.23.0"
}
},
"@actions/io": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
- "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
+ "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": {
@@ -22819,9 +23065,9 @@
}
},
"@babel/code-frame": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
- "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "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",
@@ -22836,20 +23082,20 @@
"dev": true
},
"@babel/core": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
- "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
+ "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.28.6",
- "@babel/generator": "^7.28.6",
+ "@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.28.6",
+ "@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^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",
@@ -22892,13 +23138,13 @@
}
},
"@babel/generator": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz",
- "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
+ "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.28.6",
- "@babel/types": "^7.28.6",
+ "@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"
@@ -23051,9 +23297,9 @@
}
},
"@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "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": {
@@ -23128,12 +23374,12 @@
}
},
"@babel/parser": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
- "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+ "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.28.6"
+ "@babel/types": "^7.29.0"
}
},
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
@@ -23206,12 +23452,12 @@
"requires": {}
},
"@babel/plugin-syntax-flow": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
- "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
+ "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.25.9"
+ "@babel/helper-plugin-utils": "^7.28.6"
}
},
"@babel/plugin-syntax-import-assertions": {
@@ -23436,13 +23682,13 @@
}
},
"@babel/plugin-transform-flow-strip-types": {
- "version": "7.26.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz",
- "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==",
+ "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.26.5",
- "@babel/plugin-syntax-flow": "^7.26.0"
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-flow": "^7.27.1"
}
},
"@babel/plugin-transform-for-of": {
@@ -23901,6 +24147,14 @@
"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"
+ }
+ },
"@babel/template": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
@@ -23913,24 +24167,24 @@
}
},
"@babel/traverse": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz",
- "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
+ "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.28.6",
- "@babel/generator": "^7.28.6",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.6",
+ "@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
- "@babel/types": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
}
},
"@babel/types": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
- "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+ "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",
@@ -24216,9 +24470,9 @@
"optional": true
},
"@google-cloud/storage": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.18.0.tgz",
- "integrity": "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==",
+ "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",
@@ -24227,7 +24481,7 @@
"abort-controller": "^3.0.0",
"async-retry": "^1.3.3",
"duplexify": "^4.1.3",
- "fast-xml-parser": "^4.4.1",
+ "fast-xml-parser": "^5.3.4",
"gaxios": "^6.0.2",
"google-auth-library": "^9.6.3",
"html-entities": "^2.5.2",
@@ -24290,9 +24544,9 @@
"requires": {}
},
"@grpc/grpc-js": {
- "version": "1.14.2",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.2.tgz",
- "integrity": "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA==",
+ "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",
@@ -24513,7 +24767,7 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
@@ -24527,25 +24781,25 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "dev": true
+ "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==",
- "dev": true
+ "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==",
- "dev": true
+ "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==",
- "dev": true,
+ "devOptional": true,
"requires": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
@@ -24556,7 +24810,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"ansi-regex": "^6.0.1"
}
@@ -24565,7 +24819,7 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
@@ -25281,26 +25535,64 @@
"integrity": "sha512-Bb+qLtXQ/1SA2Ck6JLVhfD9JQf6cCwgeDZZJjcIdHzUtdPTFu1hj51xdD7tUCL47Ed2i3aAx6K/M6AjLWYVs3A=="
},
"@parse/node-apn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.0.1.tgz",
- "integrity": "sha512-2xBiaznvupLOoXFaxWxcWcqCGlRn9rvqeAQnv8ogL8hZPe1Rd0es+F8ppE7g4QIy5DPJv0R4fruB8amGM6K/qA==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.1.0.tgz",
+ "integrity": "sha512-a40P5nScLDi9Pf7koKKkbwI73px0q+iLaKYNrr7kyKJebq/4duGOy3mMevZS0zltn171k3jB5BWCC27dPGsMmw==",
"requires": {
"debug": "4.4.3",
- "jsonwebtoken": "9.0.2",
+ "jsonwebtoken": "9.0.3",
"node-forge": "1.3.2",
"verror": "1.10.1"
+ },
+ "dependencies": {
+ "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"
+ }
+ },
+ "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"
+ }
+ }
}
},
"@parse/push-adapter": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.2.0.tgz",
- "integrity": "sha512-z5RB1TwNELNSvummTVP1fgncOT424j13HeKxsGHpAftUmjE+hUtSsIeG49chD0gac22Zmrk+7flYjRwQpUs6+w==",
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-8.3.1.tgz",
+ "integrity": "sha512-UOkL0bTOtUx4R866XtBwGGYvkNLQrQPrPWC4uzpbd9vR8tbfYIQQvBDT7eg58GryLb4EG+NOP8enm/VkhbwMVw==",
"requires": {
- "@parse/node-apn": "7.0.1",
- "expo-server-sdk": "4.0.0",
- "firebase-admin": "13.6.0",
+ "@parse/node-apn": "7.1.0",
+ "expo-server-sdk": "5.0.0",
+ "firebase-admin": "13.6.1",
"npmlog": "7.0.1",
- "parse": "8.0.3",
+ "parse": "8.2.0",
"web-push": "3.6.7"
}
},
@@ -25716,15 +26008,6 @@
}
}
},
- "agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "requires": {
- "debug": "^4.3.4"
- }
- },
"ansi-escapes": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
@@ -25912,16 +26195,6 @@
}
}
},
- "https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "dev": true,
- "requires": {
- "agent-base": "^7.0.2",
- "debug": "4"
- }
- },
"human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
@@ -26257,15 +26530,6 @@
"integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==",
"dev": true
},
- "agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "requires": {
- "debug": "^4.3.4"
- }
- },
"aggregate-error": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz",
@@ -26305,16 +26569,6 @@
"unicorn-magic": "^0.1.0"
}
},
- "https-proxy-agent": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
- "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
- "dev": true,
- "requires": {
- "agent-base": "^7.0.2",
- "debug": "4"
- }
- },
"indent-string": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
@@ -26486,9 +26740,9 @@
}
},
"@semantic-release/release-notes-generator": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz",
- "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==",
+ "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",
@@ -27402,13 +27656,9 @@
"requires": {}
},
"agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "optional": true,
- "requires": {
- "debug": "4"
- }
+ "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",
@@ -27691,7 +27941,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "devOptional": true
},
"base64-js": {
"version": "1.5.1",
@@ -27710,9 +27960,9 @@
"dev": true
},
"bignumber.js": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
- "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
+ "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",
@@ -28403,9 +28653,9 @@
}
},
"commander": {
- "version": "14.0.2",
- "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
- "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="
+ "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",
@@ -28526,9 +28776,9 @@
}
},
"core-js-pure": {
- "version": "3.46.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.46.0.tgz",
- "integrity": "sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw=="
+ "version": "3.48.0",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.48.0.tgz",
+ "integrity": "sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw=="
},
"core-util-is": {
"version": "1.0.3",
@@ -28578,7 +28828,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -28611,7 +28861,7 @@
"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==",
- "dev": true
+ "devOptional": true
},
"debug": {
"version": "4.4.3",
@@ -29056,7 +29306,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true
+ "devOptional": true
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
@@ -29454,9 +29704,9 @@
}
},
"eslint-plugin-unused-imports": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz",
- "integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==",
+ "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": {}
},
@@ -29587,41 +29837,19 @@
}
},
"expo-server-sdk": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-4.0.0.tgz",
- "integrity": "sha512-zi83XtG2pqyP3gyn1JIRYkydo2i6HU3CYaWo/VvhZG/F29U+QIDv6LBEUsWf4ddZlVE7c9WN1N8Be49rHgO8OQ==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/expo-server-sdk/-/expo-server-sdk-5.0.0.tgz",
+ "integrity": "sha512-GEp1XYLU80iS/hdRo3c2n092E8TgTXcHSuw6Lw68dSoWaAgiLPI2R+e5hp5+hGF1TtJZOi2nxtJX63+XA3iz9g==",
"requires": {
- "node-fetch": "^2.6.0",
"promise-limit": "^2.7.0",
- "promise-retry": "^2.0.1"
+ "promise-retry": "^2.0.1",
+ "undici": "^7.2.0"
},
"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==",
- "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=="
- },
- "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=="
- },
- "whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "requires": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
+ "undici": {
+ "version": "7.22.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
+ "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="
}
}
},
@@ -29676,10 +29904,12 @@
}
},
"express-rate-limit": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
- "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
- "requires": {}
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
+ "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==",
+ "requires": {
+ "ip-address": "10.0.1"
+ }
},
"extend": {
"version": "3.0.2",
@@ -29742,12 +29972,12 @@
"dev": true
},
"fast-xml-parser": {
- "version": "4.5.3",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
- "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.7.tgz",
+ "integrity": "sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA==",
"optional": true,
"requires": {
- "strnum": "^1.1.1"
+ "strnum": "^2.1.2"
}
},
"fastq": {
@@ -29785,7 +30015,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
@@ -30000,9 +30230,9 @@
}
},
"firebase-admin": {
- "version": "13.6.0",
- "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.0.tgz",
- "integrity": "sha512-GdPA/t0+Cq8p1JnjFRBmxRxAGvF/kl2yfdhALl38PrRp325YxyQ5aNaHui0XmaKcKiGRFIJ/EgBNWFoDP0onjw==",
+ "version": "13.6.1",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.6.1.tgz",
+ "integrity": "sha512-Zgc6yPtmPxAZo+FoK6LMG6zpSEsoSK8ifIR+IqF4oWuC3uWZU40OjxgfLTSFcsRlj/k/wD66zNv2UiTRreCNSw==",
"requires": {
"@fastify/busboy": "^3.0.0",
"@firebase/database-compat": "^2.0.0",
@@ -30086,7 +30316,7 @@
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"fetch-blob": "^3.1.2"
}
@@ -30229,20 +30459,6 @@
"uuid": "^9.0.1"
},
"dependencies": {
- "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=="
- },
- "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"
- }
- },
"node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -30277,6 +30493,115 @@
}
}
},
+ "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
+ },
+ "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==",
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ }
+ },
+ "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",
@@ -30435,9 +30760,9 @@
}
},
"globals": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
- "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
"dev": true
},
"globby": {
@@ -30494,6 +30819,11 @@
"json-bigint": "^1.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=="
+ },
"jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
@@ -30575,9 +30905,11 @@
}
},
"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=="
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "optional": true,
+ "peer": true
},
"gopd": {
"version": "1.2.0",
@@ -30850,17 +31182,6 @@
"requires": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
- },
- "dependencies": {
- "agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "dev": true,
- "requires": {
- "debug": "^4.3.4"
- }
- }
}
},
"http2-wrapper": {
@@ -30874,12 +31195,11 @@
}
},
"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,
+ "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": "6",
+ "agent-base": "^7.1.2",
"debug": "4"
}
},
@@ -30994,6 +31314,11 @@
"p-is-promise": "^3.0.0"
}
},
+ "ip-address": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
+ "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="
+ },
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -31147,7 +31472,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "devOptional": true
},
"issue-parser": {
"version": "7.0.1",
@@ -31294,7 +31619,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@isaacs/cliui": "^8.0.2",
"@pkgjs/parseargs": "^0.11.0"
@@ -32466,7 +32791,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "dev": true
+ "devOptional": true
},
"minizlib": {
"version": "2.1.2",
@@ -32634,6 +32959,17 @@
"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",
@@ -32675,6 +33011,44 @@
"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",
@@ -32686,6 +33060,45 @@
"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",
@@ -32796,7 +33209,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
- "dev": true
+ "devOptional": true
},
"node-emoji": {
"version": "2.2.0",
@@ -35017,7 +35430,7 @@
"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==",
- "dev": true
+ "devOptional": true
},
"param-case": {
"version": "3.0.4",
@@ -35039,26 +35452,18 @@
}
},
"parse": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/parse/-/parse-8.0.3.tgz",
- "integrity": "sha512-WQPrnfnXy6/p25OFD6qOAVK9hIhhU882Nw1AW5RjAJbO2G7YqChJxBgL94aexsaTnP9ajVzjGISSQ+mESrkMIA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/parse/-/parse-8.2.0.tgz",
+ "integrity": "sha512-jSx4zIqCja6O2HKhkzZ6JTm4fBUQS6sQpvFCAsqzaU4XlEhoRLm9mM1tZeYhvxTVA6zymvj/EJZ4YDOXCTRbmA==",
"requires": {
"@babel/runtime": "7.28.6",
- "@babel/runtime-corejs3": "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"
},
"dependencies": {
- "@babel/runtime-corejs3": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.6.tgz",
- "integrity": "sha512-kz2fAQ5UzjV7X7D3ySxmj3vRq89dTpqOZWv76Z6pNPztkwb/0Yj1Mtx1xFrYj6mbIHysxtBot8J4o0JLCblcFw==",
- "requires": {
- "core-js-pure": "^3.43.0"
- }
- },
"ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
@@ -35139,7 +35544,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
+ "devOptional": true
},
"path-parse": {
"version": "1.0.7",
@@ -35151,7 +35556,7 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@@ -35216,11 +35621,11 @@
"integrity": "sha512-jO/oJOununpx8DzKgvSsWm61P8JjwXlaxSlbbfTBo1nvSWoo/+I6qZYaSN96jm/KDwa5d+JMQwPGgcP6HXDRow=="
},
"pg-monitor": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-3.0.0.tgz",
- "integrity": "sha512-62jezmq3lR+lKCIsi9BXVg8Fxv+JG5LtaAuUmex5EVnBPlvAU7Ad6dOiQXHtH1xNh/Oy6Hux36k8uIjZWNeWtQ==",
+ "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"
+ "picocolors": "1.1.1"
}
},
"pg-pool": {
@@ -35479,9 +35884,9 @@
"dev": true
},
"prettier": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
- "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
+ "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": {
@@ -35614,9 +36019,9 @@
"dev": true
},
"qs": {
- "version": "6.14.1",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
- "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "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"
}
@@ -36466,7 +36871,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@@ -36475,7 +36880,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
+ "devOptional": true
},
"showdown": {
"version": "2.1.0",
@@ -36804,7 +37209,7 @@
"version": "npm:string-width@4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -36842,7 +37247,7 @@
"version": "npm:strip-ansi@6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
+ "devOptional": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -36875,9 +37280,9 @@
"dev": true
},
"strnum": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
- "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
+ "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
"optional": true
},
"stubs": {
@@ -37046,6 +37451,15 @@
"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",
@@ -37057,6 +37471,16 @@
"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",
@@ -37406,9 +37830,9 @@
}
},
"typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true
},
"typescript-eslint": {
@@ -37524,6 +37948,12 @@
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
+ "undici": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
+ "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
+ "dev": true
+ },
"undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
@@ -37714,23 +38144,6 @@
"minimist": "^1.2.5"
},
"dependencies": {
- "agent-base": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
- "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
- "requires": {
- "debug": "^4.3.4"
- }
- },
- "https-proxy-agent": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
- "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
- "requires": {
- "agent-base": "^7.0.2",
- "debug": "4"
- }
- },
"jwa": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
@@ -37756,7 +38169,7 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
- "dev": true
+ "devOptional": true
},
"webidl-conversions": {
"version": "7.0.0",
@@ -37796,7 +38209,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -37950,7 +38363,7 @@
"version": "npm:wrap-ansi@7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -37961,7 +38374,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -37970,7 +38383,7 @@
"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,
+ "devOptional": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -37979,7 +38392,7 @@
"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
+ "devOptional": true
}
}
},
@@ -38030,9 +38443,9 @@
"dev": true
},
"yaml": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true
},
"yargs": {
diff --git a/package.json b/package.json
index f01b480c2d..08427f4f51 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "parse-server",
- "version": "9.2.0",
+ "version": "9.3.0-alpha.9",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -10,7 +10,7 @@
"files": [
"bin/",
"lib/",
- "public_html/",
+ "public/",
"views/",
"LICENSE",
"NOTICE",
@@ -26,13 +26,13 @@
"@graphql-tools/schema": "10.0.23",
"@graphql-tools/utils": "10.8.6",
"@parse/fs-files-adapter": "3.0.0",
- "@parse/push-adapter": "8.2.0",
+ "@parse/push-adapter": "8.3.1",
"bcryptjs": "3.0.3",
- "commander": "14.0.2",
+ "commander": "14.0.3",
"cors": "2.8.6",
"deepcopy": "2.1.0",
"express": "5.2.1",
- "express-rate-limit": "7.5.1",
+ "express-rate-limit": "8.2.1",
"follow-redirects": "1.15.9",
"graphql": "16.11.0",
"graphql-list-fields": "2.0.4",
@@ -48,9 +48,9 @@
"mongodb": "7.0.0",
"mustache": "4.2.0",
"otpauth": "9.4.0",
- "parse": "8.0.3",
+ "parse": "8.2.0",
"path-to-regexp": "8.3.0",
- "pg-monitor": "3.0.0",
+ "pg-monitor": "3.1.0",
"pg-promise": "12.6.0",
"pluralize": "8.0.0",
"punycode": "2.3.1",
@@ -65,13 +65,13 @@
"ws": "8.18.2"
},
"devDependencies": {
- "@actions/core": "1.11.1",
+ "@actions/core": "3.0.0",
"@apollo/client": "3.13.8",
"@babel/cli": "7.27.0",
- "@babel/core": "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.26.5",
+ "@babel/plugin-transform-flow-strip-types": "7.27.1",
"@babel/preset-env": "7.27.2",
"@babel/preset-typescript": "7.27.1",
"@saithodev/semantic-release-backmerge": "4.0.1",
@@ -80,7 +80,7 @@
"@semantic-release/git": "10.0.1",
"@semantic-release/github": "11.0.3",
"@semantic-release/npm": "12.0.1",
- "@semantic-release/release-notes-generator": "14.0.3",
+ "@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",
@@ -88,9 +88,9 @@
"deep-diff": "1.0.2",
"eslint": "9.27.0",
"eslint-plugin-expect-type": "0.6.2",
- "eslint-plugin-unused-imports": "4.3.0",
+ "eslint-plugin-unused-imports": "4.4.1",
"form-data": "4.0.5",
- "globals": "16.2.0",
+ "globals": "17.3.0",
"graphql-tag": "2.12.6",
"jasmine": "5.7.1",
"jasmine-spec-reporter": "7.0.0",
@@ -105,11 +105,11 @@
"node-abort-controller": "3.1.1",
"node-fetch": "3.2.10",
"nyc": "17.1.0",
- "prettier": "2.0.5",
+ "prettier": "3.8.1",
"semantic-release": "24.2.5",
- "typescript": "5.8.3",
+ "typescript": "5.9.3",
"typescript-eslint": "8.53.1",
- "yaml": "2.8.0"
+ "yaml": "2.8.2"
},
"scripts": {
"ci:check": "node ./ci/ciCheck.js",
diff --git a/resources/buildConfigDefinitions.js b/resources/buildConfigDefinitions.js
index 8979e2658b..ae7246fd9b 100644
--- a/resources/buildConfigDefinitions.js
+++ b/resources/buildConfigDefinitions.js
@@ -158,6 +158,11 @@ function mapperFor(elt, t) {
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') {
diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js
index 7301ab54c1..d8c646382c 100644
--- a/spec/AuthenticationAdaptersV2.spec.js
+++ b/spec/AuthenticationAdaptersV2.spec.js
@@ -76,6 +76,41 @@ describe('Auth Adapter features', () => {
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',
@@ -1302,4 +1337,280 @@ describe('Auth Adapter features', () => {
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');
+ });
});
diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js
index f5b6812699..210a503fb8 100644
--- a/spec/Deprecator.spec.js
+++ b/spec/Deprecator.spec.js
@@ -70,4 +70,37 @@ describe('Deprecator', () => {
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',
+ })
+ );
+ });
});
diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js
index a0834196af..82114760af 100644
--- a/spec/EmailVerificationToken.spec.js
+++ b/spec/EmailVerificationToken.spec.js
@@ -288,7 +288,15 @@ describe('Email Verification Token Expiration:', () => {
};
const verifyUserEmails = {
method(req) {
- expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']);
+ expect(Object.keys(req)).toEqual([
+ 'original',
+ 'object',
+ 'master',
+ 'ip',
+ 'installationId',
+ 'createdWith',
+ ]);
+ expect(req.createdWith).toEqual({ action: 'signup', authProvider: 'password' });
return false;
},
};
@@ -349,7 +357,15 @@ describe('Email Verification Token Expiration:', () => {
};
const verifyUserEmails = {
method(req) {
- expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']);
+ 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;
}
@@ -384,6 +400,144 @@ describe('Email Verification Token Expiration:', () => {
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: () => {},
@@ -779,6 +933,7 @@ describe('Email Verification Token Expiration:', () => {
expect(params.master).toBeDefined();
expect(params.installationId).toBeDefined();
expect(params.resendRequest).toBeTrue();
+ expect(params.createdWith).toBeUndefined();
return true;
},
};
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/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js
index 57f94c2dab..6a274125bc 100644
--- a/spec/GridFSBucketStorageAdapter.spec.js
+++ b/spec/GridFSBucketStorageAdapter.spec.js
@@ -476,6 +476,31 @@ describe_only_db('mongo')('GridFSBucket', () => {
}
});
+ 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);
diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js
index 009254dfcc..db2250f15a 100644
--- a/spec/PagesRouter.spec.js
+++ b/spec/PagesRouter.spec.js
@@ -225,9 +225,7 @@ describe('Pages Router', () => {
expect(Config.get(Parse.applicationId).pages.forceRedirect).toBe(
Definitions.PagesOptions.forceRedirect.default
);
- expect(Config.get(Parse.applicationId).pages.pagesPath).toBe(
- Definitions.PagesOptions.pagesPath.default
- );
+ expect(Config.get(Parse.applicationId).pages.pagesPath).toBeUndefined();
expect(Config.get(Parse.applicationId).pages.pagesEndpoint).toBe(
Definitions.PagesOptions.pagesEndpoint.default
);
@@ -1181,6 +1179,91 @@ describe('Pages Router', () => {
});
});
+ 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',
+ pages: { enableRouter: true },
+ });
+
+ 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',
+ pages: { enableRouter: true },
+ });
+
+ 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',
+ pages: { enableRouter: true },
+ });
+
+ // 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('XSS Protection', () => {
beforeEach(async () => {
await reconfigureServer({
diff --git a/spec/ParseACL.spec.js b/spec/ParseACL.spec.js
index d8abc65c06..f2d2cbd8b3 100644
--- a/spec/ParseACL.spec.js
+++ b/spec/ParseACL.spec.js
@@ -951,4 +951,66 @@ describe('Parse.ACL', () => {
expect(acl[user.id].write).toBeTrue();
expect(acl[user.id].read).toBeTrue();
});
+
+ it('should not overwrite ACL with defaultACL on update', async () => {
+ await new Parse.Object('TestObject').save();
+ const schema = await Parse.Server.database.loadSchema();
+ await schema.updateClass(
+ 'TestObject',
+ {},
+ {
+ create: { '*': true },
+ update: { '*': true },
+ addField: { '*': true },
+ ACL: {
+ '*': { read: true },
+ currentUser: { read: true, write: true },
+ },
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ const obj = new Parse.Object('TestObject');
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+
+ const originalAcl = obj.getACL().toJSON();
+ expect(originalAcl['*']).toEqual({ read: true });
+ expect(originalAcl[user.id]).toEqual({ read: true, write: true });
+
+ obj.set('field', 'value');
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+
+ const updatedAcl = obj.getACL().toJSON();
+ expect(updatedAcl).toEqual(originalAcl);
+ });
+
+ it('should allow explicit ACL modification on update', async () => {
+ await new Parse.Object('TestObject').save();
+ const schema = await Parse.Server.database.loadSchema();
+ await schema.updateClass(
+ 'TestObject',
+ {},
+ {
+ create: { '*': true },
+ update: { '*': true },
+ ACL: {
+ '*': { read: true },
+ currentUser: { read: true, write: true },
+ },
+ }
+ );
+ const user = await Parse.User.signUp('testuser', 'p@ssword');
+ const obj = new Parse.Object('TestObject');
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+
+ const customAcl = new Parse.ACL();
+ customAcl.setPublicReadAccess(false);
+ customAcl.setReadAccess(user.id, true);
+ customAcl.setWriteAccess(user.id, true);
+ obj.setACL(customAcl);
+ await obj.save(null, { sessionToken: user.getSessionToken() });
+
+ const updatedAcl = obj.getACL().toJSON();
+ expect(updatedAcl['*']).toBeUndefined();
+ expect(updatedAcl[user.id]).toEqual({ read: true, write: true });
+ });
});
diff --git a/spec/ParseFile.spec.js b/spec/ParseFile.spec.js
index 5c1c3c99e7..da69edc416 100644
--- a/spec/ParseFile.spec.js
+++ b/spec/ParseFile.spec.js
@@ -1368,6 +1368,34 @@ describe('Parse.File testing', () => {
},
})
).toBeRejectedWith('fileUpload.fileExtensions must be an array.');
+ await expectAsync(
+ reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: 'not-an-array',
+ },
+ })
+ ).toBeRejectedWith('fileUpload.allowedFileUrlDomains must be an array.');
+ await expectAsync(
+ reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [123],
+ },
+ })
+ ).toBeRejectedWith('fileUpload.allowedFileUrlDomains must contain only non-empty strings.');
+ await expectAsync(
+ reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [''],
+ },
+ })
+ ).toBeRejectedWith('fileUpload.allowedFileUrlDomains must contain only non-empty strings.');
+ await expectAsync(
+ reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: ['example.com'],
+ },
+ })
+ ).toBeResolved();
});
});
@@ -1625,4 +1653,504 @@ describe('Parse.File testing', () => {
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/);
});
});
+
+ describe('File URL domain validation for SSRF prevention', () => {
+ it('rejects cloud function call with disallowed file URL', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ Parse.Cloud.define('setUserIcon', () => {});
+
+ await expectAsync(
+ Parse.Cloud.run('setUserIcon', {
+ file: { __type: 'File', name: 'file.txt', url: 'http://malicious.example.com/leak' },
+ })
+ ).toBeRejectedWith(
+ jasmine.objectContaining({ message: jasmine.stringMatching(/not allowed/) })
+ );
+ });
+
+ it('rejects REST API create with disallowed file URL', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ await expectAsync(
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ file: {
+ __type: 'File',
+ name: 'test.txt',
+ url: 'http://malicious.example.com/file',
+ },
+ },
+ })
+ ).toBeRejectedWith(jasmine.objectContaining({ status: 400 }));
+ });
+
+ it('rejects REST API update with disallowed file URL', async () => {
+ const obj = new Parse.Object('TestObject');
+ await obj.save();
+
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ await expectAsync(
+ request({
+ method: 'PUT',
+ url: `http://localhost:8378/1/classes/TestObject/${obj.id}`,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ file: {
+ __type: 'File',
+ name: 'test.txt',
+ url: 'http://malicious.example.com/file',
+ },
+ },
+ })
+ ).toBeRejectedWith(jasmine.objectContaining({ status: 400 }));
+ });
+
+ it('allows file URLs matching configured domains', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: ['cdn.example.com'],
+ },
+ });
+
+ Parse.Cloud.define('setUserIcon', () => 'ok');
+
+ const result = await Parse.Cloud.run('setUserIcon', {
+ file: { __type: 'File', name: 'file.txt', url: 'http://cdn.example.com/file.txt' },
+ });
+ expect(result).toBe('ok');
+ });
+
+ it('allows file URLs when default wildcard is used', async () => {
+ Parse.Cloud.define('setUserIcon', () => 'ok');
+
+ const result = await Parse.Cloud.run('setUserIcon', {
+ file: { __type: 'File', name: 'file.txt', url: 'http://example.com/file.txt' },
+ });
+ expect(result).toBe('ok');
+ });
+
+ it('allows files with server-hosted URLs even when domains are restricted', async () => {
+ const file = new Parse.File('test.txt', [1, 2, 3]);
+ await file.save();
+
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: ['localhost'],
+ },
+ });
+
+ const result = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ file: {
+ __type: 'File',
+ name: file.name(),
+ url: file.url(),
+ },
+ },
+ });
+ expect(result.status).toBe(201);
+ });
+
+ it('allows REST API create with file URL when default wildcard is used', async () => {
+ const result = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ file: {
+ __type: 'File',
+ name: 'test.txt',
+ url: 'http://example.com/file.txt',
+ },
+ },
+ });
+ expect(result.status).toBe(201);
+ });
+
+ it('allows cloud function with name-only file when domains are restricted', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ Parse.Cloud.define('processFile', req => req.params.file.name());
+
+ const result = await Parse.Cloud.run('processFile', {
+ file: { __type: 'File', name: 'test.txt' },
+ });
+ expect(result).toBe('test.txt');
+ });
+
+ it('rejects disallowed file URL in array field', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ await expectAsync(
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ files: [
+ {
+ __type: 'File',
+ name: 'test.txt',
+ url: 'http://malicious.example.com/file',
+ },
+ ],
+ },
+ })
+ ).toBeRejectedWith(jasmine.objectContaining({ status: 400 }));
+ });
+
+ it('rejects disallowed file URL nested in object', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+
+ await expectAsync(
+ request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/classes/TestObject',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ },
+ body: {
+ data: {
+ nested: {
+ file: {
+ __type: 'File',
+ name: 'test.txt',
+ url: 'http://malicious.example.com/file',
+ },
+ },
+ },
+ },
+ })
+ ).toBeRejectedWith(jasmine.objectContaining({ status: 400 }));
+ });
+ });
+
+ describe('streaming binary uploads', () => {
+ afterEach(() => {
+ Parse.Cloud._removeAllHooks();
+ });
+
+ describe('createSizeLimitedStream', () => {
+ const { createSizeLimitedStream } = require('../lib/Routers/FilesRouter');
+ const { Readable } = require('stream');
+
+ it('passes data through when under limit', async () => {
+ const input = Readable.from(Buffer.from('hello'));
+ const limited = createSizeLimitedStream(input, 100);
+ const chunks = [];
+ for await (const chunk of limited) {
+ chunks.push(chunk);
+ }
+ expect(Buffer.concat(chunks).toString()).toBe('hello');
+ });
+
+ it('destroys stream when data exceeds limit', async () => {
+ const input = Readable.from(Buffer.from('hello world, this is too long'));
+ const limited = createSizeLimitedStream(input, 5);
+ const chunks = [];
+ try {
+ for await (const chunk of limited) {
+ chunks.push(chunk);
+ }
+ fail('should have thrown');
+ } catch (e) {
+ expect(e.message).toContain('exceeds');
+ }
+ });
+
+ });
+
+ it('streams binary upload with X-Parse-Upload-Mode header', async () => {
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ let response;
+ try {
+ response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/stream-test.txt',
+ body: 'streaming file content',
+ });
+ } catch (e) {
+ fail('Request failed: status=' + e.status + ' text=' + e.text + ' data=' + JSON.stringify(e.data));
+ return;
+ }
+ const b = response.data;
+ expect(b.name).toMatch(/_stream-test.txt$/);
+ expect(b.url).toMatch(/stream-test\.txt$/);
+ const getResponse = await request({ url: b.url });
+ expect(getResponse.text).toEqual('streaming file content');
+ });
+
+ it('infers content type from extension when Content-Type header is missing', async () => {
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ const response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/inferred.txt',
+ body: 'inferred content type',
+ });
+ const b = response.data;
+ expect(b.name).toMatch(/_inferred.txt$/);
+ const getResponse = await request({ url: b.url });
+ expect(getResponse.text).toEqual('inferred content type');
+ });
+
+ it('uses buffered path without X-Parse-Upload-Mode header', async () => {
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ };
+ const response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/buffered-test.txt',
+ body: 'buffered file content',
+ });
+ const b = response.data;
+ expect(b.name).toMatch(/_buffered-test.txt$/);
+ const getResponse = await request({ url: b.url });
+ expect(getResponse.text).toEqual('buffered file content');
+ });
+
+ it('rejects streaming upload exceeding size limit', async () => {
+ await reconfigureServer({ maxUploadSize: '10b' });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ try {
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/big-file.txt',
+ body: 'this content is definitely longer than 10 bytes',
+ });
+ fail('should have thrown');
+ } catch (response) {
+ expect(response.data.code).toBe(Parse.Error.FILE_SAVE_ERROR);
+ expect(response.data.error).toContain('exceeds');
+ }
+ });
+
+ it('rejects streaming upload with Content-Length exceeding limit', async () => {
+ await reconfigureServer({ maxUploadSize: '10b' });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ 'Content-Length': '99999',
+ };
+ try {
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/big-file.txt',
+ body: 'hi',
+ });
+ fail('should have thrown');
+ } catch (response) {
+ expect(response.data.code).toBe(Parse.Error.FILE_SAVE_ERROR);
+ expect(response.data.error).toContain('exceeds');
+ }
+ });
+
+ it('fires beforeSave trigger with request.stream = true on streaming upload', async () => {
+ let receivedStream;
+ let receivedData;
+ Parse.Cloud.beforeSave(Parse.File, (request) => {
+ receivedStream = request.stream;
+ receivedData = request.file._data;
+ request.file.addMetadata('source', 'stream');
+ request.file.addTag('env', 'test');
+ });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ const response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/trigger-test.txt',
+ body: 'trigger test content',
+ });
+ expect(response.data.name).toMatch(/_trigger-test.txt$/);
+ expect(receivedStream).toBe(true);
+ expect(receivedData).toBeFalsy();
+ const getResponse = await request({ url: response.data.url });
+ expect(getResponse.text).toEqual('trigger test content');
+ });
+
+ it('rejects streaming upload when beforeSave trigger throws', async () => {
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Upload rejected');
+ });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ try {
+ await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/rejected.txt',
+ body: 'rejected content',
+ });
+ fail('should have thrown');
+ } catch (response) {
+ expect(response.data.code).toBe(Parse.Error.SCRIPT_FAILED);
+ expect(response.data.error).toBe('Upload rejected');
+ }
+ });
+
+ it('skips save when beforeSave trigger returns Parse.File with URL on streaming upload', async () => {
+ Parse.Cloud.beforeSave(Parse.File, () => {
+ return Parse.File.fromJSON({
+ __type: 'File',
+ name: 'existing.txt',
+ url: 'http://example.com/existing.txt',
+ });
+ });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ const response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/skip-save.txt',
+ body: 'should not be saved',
+ });
+ expect(response.data.url).toBe('http://example.com/existing.txt');
+ expect(response.data.name).toBe('existing.txt');
+ });
+
+ it('fires afterSave trigger with request.stream = true on streaming upload', async () => {
+ let afterSaveStream;
+ let afterSaveData;
+ let afterSaveUrl;
+ Parse.Cloud.afterSave(Parse.File, (request) => {
+ afterSaveStream = request.stream;
+ afterSaveData = request.file._data;
+ afterSaveUrl = request.file._url;
+ });
+ const headers = {
+ 'Content-Type': 'application/octet-stream',
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-REST-API-Key': 'rest',
+ 'X-Parse-Upload-Mode': 'stream',
+ };
+ const response = await request({
+ method: 'POST',
+ headers: headers,
+ url: 'http://localhost:8378/1/files/after-save.txt',
+ body: 'after save content',
+ });
+ expect(response.data.name).toMatch(/_after-save.txt$/);
+ expect(afterSaveStream).toBe(true);
+ expect(afterSaveData).toBeFalsy();
+ expect(afterSaveUrl).toBeTruthy();
+ });
+
+ it('verifies FilesAdapter default supportsStreaming is false', () => {
+ const { FilesAdapter } = require('../lib/Adapters/Files/FilesAdapter');
+ const adapter = new FilesAdapter();
+ expect(adapter.supportsStreaming).toBe(false);
+ });
+
+ it('legacy JSON-wrapped upload still works', async () => {
+ await reconfigureServer({
+ fileUpload: {
+ enableForPublic: true,
+ fileExtensions: ['*'],
+ },
+ });
+ const response = await request({
+ method: 'POST',
+ url: 'http://localhost:8378/1/files/legacy.txt',
+ body: JSON.stringify({
+ _ApplicationId: 'test',
+ _JavaScriptKey: 'test',
+ _ContentType: 'text/plain',
+ base64: Buffer.from('legacy content').toString('base64'),
+ }),
+ });
+ const b = response.data;
+ expect(b.name).toMatch(/_legacy.txt$/);
+ const getResponse = await request({ url: b.url });
+ expect(getResponse.text).toEqual('legacy content');
+ });
+ });
});
diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js
index 3477f9d01c..40df9ea1e0 100644
--- a/spec/ParseGraphQLServer.spec.js
+++ b/spec/ParseGraphQLServer.spec.js
@@ -10240,6 +10240,52 @@ describe('ParseGraphQLServer', () => {
}
});
+ it('should reject file with disallowed URL domain', async () => {
+ try {
+ parseServer = await global.reconfigureServer({
+ publicServerURL: 'http://localhost:13377/parse',
+ fileUpload: {
+ allowedFileUrlDomains: [],
+ },
+ });
+ await createGQLFromParseServer(parseServer);
+
+ const schemaController = await parseServer.config.databaseController.loadSchema();
+ await schemaController.addClassIfNotExists('SomeClass', {
+ someField: { type: 'File' },
+ });
+ await resetGraphQLCache();
+ await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
+
+ const createResult = await apolloClient.mutate({
+ mutation: gql`
+ mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) {
+ createSomeClass(input: { fields: $fields }) {
+ someClass {
+ id
+ }
+ }
+ }
+ `,
+ variables: {
+ fields: {
+ someField: {
+ file: {
+ name: 'test.txt',
+ url: 'http://malicious.example.com/leak',
+ __type: 'File',
+ },
+ },
+ },
+ },
+ });
+ fail('should have thrown');
+ expect(createResult).toBeUndefined();
+ } catch (e) {
+ expect(e.message).toMatch(/not allowed/);
+ }
+ });
+
it('should support files on required file', async () => {
try {
parseServer = await global.reconfigureServer({
diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js
index aaae271332..48ae1d2a9c 100644
--- a/spec/ParseUser.spec.js
+++ b/spec/ParseUser.spec.js
@@ -1395,6 +1395,19 @@ describe('Parse.User testing', () => {
});
});
+ it('should return authData when select authData with masterKey', async () => {
+ const provider = getMockFacebookProvider();
+ Parse.User._registerAuthenticationProvider(provider);
+ const user = await Parse.User._logInWith('facebook');
+ const query = new Parse.Query(Parse.User);
+ query.select('authData');
+ const result = await query.get(user.id, { useMasterKey: true });
+ expect(result.get('authData')).toBeDefined();
+ expect(result.get('authData').facebook).toBeDefined();
+ expect(result.get('authData').facebook.id).toBe('8675309');
+ expect(result.get('authData').facebook.access_token).toBe('jenny');
+ });
+
it('only creates a single session for an installation / user pair (#2885)', async done => {
Parse.Object.disableSingleInstance();
const provider = getMockFacebookProvider();
diff --git a/spec/Utils.spec.js b/spec/Utils.spec.js
index a473064376..853ddc9b3c 100644
--- a/spec/Utils.spec.js
+++ b/spec/Utils.spec.js
@@ -175,6 +175,81 @@ describe('Utils', () => {
});
});
+ describe('parseSizeToBytes', () => {
+ it('parses megabyte string', () => {
+ expect(Utils.parseSizeToBytes('20mb')).toBe(20 * 1024 * 1024);
+ });
+
+ it('parses Mb string (case-insensitive)', () => {
+ expect(Utils.parseSizeToBytes('20Mb')).toBe(20 * 1024 * 1024);
+ });
+
+ it('parses kilobyte string', () => {
+ expect(Utils.parseSizeToBytes('512kb')).toBe(512 * 1024);
+ });
+
+ it('parses gigabyte string', () => {
+ expect(Utils.parseSizeToBytes('1gb')).toBe(1 * 1024 * 1024 * 1024);
+ });
+
+ it('parses bytes suffix', () => {
+ expect(Utils.parseSizeToBytes('100b')).toBe(100);
+ });
+
+ it('parses plain number as bytes', () => {
+ expect(Utils.parseSizeToBytes(1048576)).toBe(1048576);
+ });
+
+ it('parses numeric string as bytes', () => {
+ expect(Utils.parseSizeToBytes('1048576')).toBe(1048576);
+ });
+
+ it('parses decimal value and floors result', () => {
+ expect(Utils.parseSizeToBytes('1.5mb')).toBe(Math.floor(1.5 * 1024 * 1024));
+ });
+
+ it('trims whitespace around value', () => {
+ expect(Utils.parseSizeToBytes(' 20mb ')).toBe(20 * 1024 * 1024);
+ });
+
+ it('allows whitespace between number and unit', () => {
+ expect(Utils.parseSizeToBytes('20 mb')).toBe(20 * 1024 * 1024);
+ });
+
+ it('parses zero', () => {
+ expect(Utils.parseSizeToBytes('0')).toBe(0);
+ expect(Utils.parseSizeToBytes(0)).toBe(0);
+ });
+
+ it('throws on invalid string', () => {
+ expect(() => Utils.parseSizeToBytes('abc')).toThrow();
+ });
+
+ it('throws on negative value', () => {
+ expect(() => Utils.parseSizeToBytes('-5mb')).toThrow();
+ });
+
+ it('throws on empty string', () => {
+ expect(() => Utils.parseSizeToBytes('')).toThrow();
+ });
+
+ it('throws on unsupported unit', () => {
+ expect(() => Utils.parseSizeToBytes('10tb')).toThrow();
+ });
+
+ it('throws on NaN', () => {
+ expect(() => Utils.parseSizeToBytes(NaN)).toThrow();
+ });
+
+ it('throws on Infinity', () => {
+ expect(() => Utils.parseSizeToBytes(Infinity)).toThrow();
+ });
+
+ it('throws on negative number', () => {
+ expect(() => Utils.parseSizeToBytes(-1)).toThrow();
+ });
+ });
+
describe('createSanitizedError', () => {
it('should return "Permission denied" when enableSanitizedErrorResponse is true', () => {
const config = { enableSanitizedErrorResponse: true };
diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js
index e2113c2804..62d00275e7 100644
--- a/spec/ValidationAndPasswordsReset.spec.js
+++ b/spec/ValidationAndPasswordsReset.spec.js
@@ -284,6 +284,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
expect(params.ip).toBeDefined();
expect(params.master).toBeDefined();
expect(params.installationId).toBeDefined();
+ expect(params.createdWith).toEqual({ action: 'login', authProvider: 'password' });
return true;
},
};
diff --git a/spec/buildConfigDefinitions.spec.js b/spec/buildConfigDefinitions.spec.js
index f0a8055860..bc15793a04 100644
--- a/spec/buildConfigDefinitions.spec.js
+++ b/spec/buildConfigDefinitions.spec.js
@@ -133,6 +133,72 @@ describe('buildConfigDefinitions', () => {
expect(result.property.name).toBe('arrayParser');
});
+ it('should return booleanOrFunctionParser for UnionTypeAnnotation containing boolean (nullable)', () => {
+ const mockElement = {
+ type: 'UnionTypeAnnotation',
+ typeAnnotation: {
+ types: [
+ { type: 'BooleanTypeAnnotation' },
+ { type: 'FunctionTypeAnnotation' },
+ ],
+ },
+ };
+
+ const result = mapperFor(mockElement, t);
+
+ expect(t.isMemberExpression(result)).toBe(true);
+ expect(result.object.name).toBe('parsers');
+ expect(result.property.name).toBe('booleanOrFunctionParser');
+ });
+
+ it('should return booleanOrFunctionParser for UnionTypeAnnotation containing boolean (non-nullable)', () => {
+ const mockElement = {
+ type: 'UnionTypeAnnotation',
+ types: [
+ { type: 'BooleanTypeAnnotation' },
+ { type: 'FunctionTypeAnnotation' },
+ ],
+ };
+
+ const result = mapperFor(mockElement, t);
+
+ expect(t.isMemberExpression(result)).toBe(true);
+ expect(result.object.name).toBe('parsers');
+ expect(result.property.name).toBe('booleanOrFunctionParser');
+ });
+
+ it('should return undefined for UnionTypeAnnotation without boolean', () => {
+ const mockElement = {
+ type: 'UnionTypeAnnotation',
+ typeAnnotation: {
+ types: [
+ { type: 'StringTypeAnnotation' },
+ { type: 'NumberTypeAnnotation' },
+ ],
+ },
+ };
+
+ const result = mapperFor(mockElement, t);
+
+ expect(result).toBeUndefined();
+ });
+
+ it('should return undefined for UnionTypeAnnotation with boolean but without function', () => {
+ const mockElement = {
+ type: 'UnionTypeAnnotation',
+ typeAnnotation: {
+ types: [
+ { type: 'BooleanTypeAnnotation' },
+ { type: 'VoidTypeAnnotation' },
+ ],
+ },
+ };
+
+ const result = mapperFor(mockElement, t);
+
+ expect(result).toBeUndefined();
+ });
+
it('should return objectParser for unknown GenericTypeAnnotation', () => {
const mockElement = {
type: 'GenericTypeAnnotation',
diff --git a/spec/parsers.spec.js b/spec/parsers.spec.js
index 413bdb5156..a844016ba7 100644
--- a/spec/parsers.spec.js
+++ b/spec/parsers.spec.js
@@ -3,6 +3,7 @@ const {
numberOrBoolParser,
numberOrStringParser,
booleanParser,
+ booleanOrFunctionParser,
objectParser,
arrayParser,
moduleOrObjectParser,
@@ -48,6 +49,23 @@ describe('parsers', () => {
expect(parser(2)).toEqual(false);
});
+ it('parses correctly with booleanOrFunctionParser', () => {
+ const parser = booleanOrFunctionParser;
+ // Preserves functions
+ const fn = () => true;
+ expect(parser(fn)).toBe(fn);
+ const asyncFn = async () => false;
+ expect(parser(asyncFn)).toBe(asyncFn);
+ // Parses booleans and string booleans like booleanParser
+ expect(parser(true)).toEqual(true);
+ expect(parser(false)).toEqual(false);
+ expect(parser('true')).toEqual(true);
+ expect(parser('false')).toEqual(false);
+ expect(parser('1')).toEqual(true);
+ expect(parser(1)).toEqual(true);
+ expect(parser(0)).toEqual(false);
+ });
+
it('parses correctly with objectParser', () => {
const parser = objectParser;
expect(parser({ hello: 'world' })).toEqual({ hello: 'world' });
diff --git a/src/Adapters/Auth/BaseCodeAuthAdapter.js b/src/Adapters/Auth/BaseCodeAuthAdapter.js
index 696e4ee71b..52f12f8703 100644
--- a/src/Adapters/Auth/BaseCodeAuthAdapter.js
+++ b/src/Adapters/Auth/BaseCodeAuthAdapter.js
@@ -72,32 +72,47 @@ export default class BaseAuthCodeAdapter extends AuthAdapter {
throw new Error('getAccessTokenFromCode is not implemented');
}
+ /**
+ * Validates auth data on login. In the standard auth flows (login, signup,
+ * update), `beforeFind` runs first and validates credentials, so no
+ * additional credential check is needed here.
+ */
validateLogin(authData) {
- // User validation is already done in beforeFind
return {
id: authData.id,
}
}
+ /**
+ * Validates auth data on first setup or when linking a new provider.
+ * In the standard auth flows, `beforeFind` runs first and validates
+ * credentials, so no additional credential check is needed here.
+ */
validateSetUp(authData) {
- // User validation is already done in beforeFind
return {
id: authData.id,
}
}
+ /**
+ * Returns the auth data to expose to the client after a query.
+ */
afterFind(authData) {
return {
id: authData.id,
}
}
+ /**
+ * Validates auth data on update. In the standard auth flows, `beforeFind`
+ * runs first for any changed auth data and validates credentials, so no
+ * additional credential check is needed here. Unchanged (echoed-back) data
+ * skips both `beforeFind` and validation entirely.
+ */
validateUpdate(authData) {
- // User validation is already done in beforeFind
return {
id: authData.id,
}
-
}
parseResponseData(data) {
diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js
index 7f5581da49..7c4f0e7011 100755
--- a/src/Adapters/Auth/index.js
+++ b/src/Adapters/Auth/index.js
@@ -254,8 +254,25 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
);
};
+ // Returns the list of auth provider names that have a valid adapter configured.
+ // This includes both built-in providers and custom providers from authOptions.
+ const getProviders = function () {
+ const allProviders = new Set([...Object.keys(providers), ...Object.keys(authOptions)]);
+ if (!_enableAnonymousUsers) {
+ allProviders.delete('anonymous');
+ }
+ return [...allProviders].filter(provider => {
+ try {
+ return !!loadAuthAdapter(provider, authOptions);
+ } catch {
+ return false;
+ }
+ });
+ };
+
return Object.freeze({
getValidatorForProvider,
+ getProviders,
setEnableAnonymousUsers,
runAfterFind,
});
diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js
index 0e9b555853..0ada581069 100644
--- a/src/Adapters/Files/FilesAdapter.js
+++ b/src/Adapters/Files/FilesAdapter.js
@@ -26,7 +26,7 @@ export class FilesAdapter {
/** Responsible for storing the file in order to be retrieved later by its filename
*
* @param {string} filename - the filename to save
- * @param {*} data - the buffer of data from the file
+ * @param {Buffer|import('stream').Readable} data - the file data as a Buffer, or a Readable stream if the adapter supports streaming (see supportsStreaming)
* @param {string} contentType - the supposed contentType
* @discussion the contentType can be undefined if the controller was not able to determine it
* @param {object} options - (Optional) options to be passed to file adapter (S3 File Adapter Only)
@@ -38,6 +38,16 @@ export class FilesAdapter {
*/
createFile(filename: string, data, contentType: string, options: Object): Promise {}
+ /** Whether this adapter supports receiving Readable streams in createFile().
+ * If false (default), streams are buffered to a Buffer before being passed.
+ * Override and return true to receive Readable streams directly.
+ *
+ * @return {boolean}
+ */
+ get supportsStreaming() {
+ return false;
+ }
+
/** Responsible for deleting the specified file
*
* @param {string} filename - the filename to delete
diff --git a/src/Adapters/Files/GridFSBucketAdapter.js b/src/Adapters/Files/GridFSBucketAdapter.js
index 8e1a849030..18c4eaa3d6 100644
--- a/src/Adapters/Files/GridFSBucketAdapter.js
+++ b/src/Adapters/Files/GridFSBucketAdapter.js
@@ -45,6 +45,10 @@ export class GridFSBucketAdapter extends FilesAdapter {
this._mongoOptions = _mongoOptions;
}
+ get supportsStreaming() {
+ return true;
+ }
+
_connect() {
if (!this._connectionPromise) {
// Only use driverInfo if clientMetadata option is set
@@ -77,6 +81,32 @@ export class GridFSBucketAdapter extends FilesAdapter {
const stream = await bucket.openUploadStream(filename, {
metadata: options.metadata,
});
+
+ // If data is a stream and encryption is enabled, buffer first
+ // (AES-256-GCM needs complete data for format: [encrypted][IV][authTag])
+ if (typeof data?.pipe === 'function' && this._encryptionKey !== null) {
+ data = await new Promise((resolve, reject) => {
+ const chunks = [];
+ data.on('data', chunk => chunks.push(chunk));
+ data.on('end', () => resolve(Buffer.concat(chunks)));
+ data.on('error', reject);
+ });
+ }
+
+ if (typeof data?.pipe === 'function') {
+ // Pipe readable stream directly into GridFS upload stream
+ return new Promise((resolve, reject) => {
+ data.pipe(stream);
+ stream.on('finish', resolve);
+ stream.on('error', reject);
+ data.on('error', (err) => {
+ stream.destroy(err);
+ reject(err);
+ });
+ });
+ }
+
+ // Buffer path (existing behavior)
if (this._encryptionKey !== null) {
try {
const iv = crypto.randomBytes(16);
diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
index 7eaafcbde2..e988c9cc19 100644
--- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
+++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
@@ -1607,20 +1607,28 @@ export class PostgresStorageAdapter implements StorageAdapter {
const generate = (jsonb: string, key: string, value: any) => {
return `json_object_set_key(COALESCE(${jsonb}, '{}'::jsonb), ${key}, ${value})::jsonb`;
};
+ const generateRemove = (jsonb: string, key: string) => {
+ return `(COALESCE(${jsonb}, '{}'::jsonb) - ${key})`;
+ };
const lastKey = `$${index}:name`;
const fieldNameIndex = index;
index += 1;
values.push(fieldName);
const update = Object.keys(fieldValue).reduce((lastKey: string, key: string) => {
+ let value = fieldValue[key];
+ if (value && value.__op === 'Delete') {
+ value = null;
+ }
+ if (value === null) {
+ const str = generateRemove(lastKey, `$${index}::text`);
+ values.push(key);
+ index += 1;
+ return str;
+ }
const str = generate(lastKey, `$${index}::text`, `$${index + 1}::jsonb`);
index += 2;
- let value = fieldValue[key];
if (value) {
- if (value.__op === 'Delete') {
- value = null;
- } else {
- value = JSON.stringify(value);
- }
+ value = JSON.stringify(value);
}
values.push(key, value);
return str;
diff --git a/src/Auth.js b/src/Auth.js
index d8bf7e651f..331780961f 100644
--- a/src/Auth.js
+++ b/src/Auth.js
@@ -417,15 +417,29 @@ Auth.prototype._getAllRolesNamesForRoleIds = function (roleIDs, names = [], quer
});
};
-const findUsersWithAuthData = async (config, authData, beforeFind) => {
+const findUsersWithAuthData = async (config, authData, beforeFind, currentUserAuthData) => {
const providers = Object.keys(authData);
const queries = await Promise.all(
providers.map(async provider => {
const providerAuthData = authData[provider];
+ // Skip providers being unlinked (null value)
+ if (providerAuthData === null) {
+ return null;
+ }
+
+ // Skip beforeFind only when incoming data is confirmed unchanged from stored data.
+ // This handles echoed-back authData from afterFind (e.g. client sends back { id: 'x' }
+ // alongside a provider unlink). On login/signup, currentUserAuthData is undefined so
+ // beforeFind always runs, preserving it as the security gate for missing credentials.
+ const storedProviderData = currentUserAuthData?.[provider];
+ const incomingKeys = Object.keys(providerAuthData || {});
+ const isUnchanged = storedProviderData && incomingKeys.length > 0 &&
+ !incomingKeys.some(key => !isDeepStrictEqual(providerAuthData[key], storedProviderData[key]));
+
const adapter = config.authDataManager.getValidatorForProvider(provider)?.adapter;
- if (beforeFind && typeof adapter?.beforeFind === 'function') {
+ if (beforeFind && typeof adapter?.beforeFind === 'function' && !isUnchanged) {
await adapter.beforeFind(providerAuthData);
}
@@ -456,7 +470,32 @@ const hasMutatedAuthData = (authData, userAuthData) => {
if (provider === 'anonymous') { return; }
const providerData = authData[provider];
const userProviderAuthData = userAuthData[provider];
- if (!isDeepStrictEqual(providerData, userProviderAuthData)) {
+
+ // If unlinking (setting to null), consider it mutated
+ if (providerData === null) {
+ mutatedAuthData[provider] = providerData;
+ return;
+ }
+
+ // If provider doesn't exist in stored data, it's new
+ if (!userProviderAuthData) {
+ mutatedAuthData[provider] = providerData;
+ return;
+ }
+
+ // Check if incoming data represents actual changes vs just echoing back
+ // what afterFind returned. If incoming data is a subset of stored data
+ // (all incoming fields match stored values), it's not mutated.
+ // If incoming data has different values or fields not in stored data, it's mutated.
+ // This handles the case where afterFind strips sensitive fields like 'code':
+ // - Incoming: { id: 'x' }, Stored: { id: 'x', code: 'secret' } -> NOT mutated (subset)
+ // - Incoming: { id: 'x', token: 'new' }, Stored: { id: 'x', token: 'old' } -> MUTATED
+ const incomingKeys = Object.keys(providerData || {});
+ const hasChanges = incomingKeys.some(key => {
+ return !isDeepStrictEqual(providerData[key], userProviderAuthData[key]);
+ });
+
+ if (hasChanges) {
mutatedAuthData[provider] = providerData;
}
});
diff --git a/src/Config.js b/src/Config.js
index 54e3cc5ca4..8af67cd543 100644
--- a/src/Config.js
+++ b/src/Config.js
@@ -326,9 +326,7 @@ export class Config {
} else if (!isBoolean(pages.forceRedirect)) {
throw 'Parse Server option pages.forceRedirect must be a boolean.';
}
- if (pages.pagesPath === undefined) {
- pages.pagesPath = PagesOptions.pagesPath.default;
- } else if (!isString(pages.pagesPath)) {
+ if (pages.pagesPath !== undefined && !isString(pages.pagesPath)) {
throw 'Parse Server option pages.pagesPath must be a string.';
}
if (pages.pagesEndpoint === undefined) {
@@ -552,6 +550,17 @@ export class Config {
} else if (!Array.isArray(fileUpload.fileExtensions)) {
throw 'fileUpload.fileExtensions must be an array.';
}
+ if (fileUpload.allowedFileUrlDomains === undefined) {
+ fileUpload.allowedFileUrlDomains = FileUploadOptions.allowedFileUrlDomains.default;
+ } else if (!Array.isArray(fileUpload.allowedFileUrlDomains)) {
+ throw 'fileUpload.allowedFileUrlDomains must be an array.';
+ } else {
+ for (const domain of fileUpload.allowedFileUrlDomains) {
+ if (typeof domain !== 'string' || domain === '') {
+ throw 'fileUpload.allowedFileUrlDomains must contain only non-empty strings.';
+ }
+ }
+ }
}
static validateIps(field, masterKeyIps) {
diff --git a/src/Controllers/AdaptableController.js b/src/Controllers/AdaptableController.js
index 15551a6e38..610b48b3fd 100644
--- a/src/Controllers/AdaptableController.js
+++ b/src/Controllers/AdaptableController.js
@@ -48,6 +48,11 @@ export class AdaptableController {
// Makes sure the prototype matches
const mismatches = Object.getOwnPropertyNames(Type.prototype).reduce((obj, key) => {
+ // Skip getters — they provide optional defaults that adapters don't need to implement
+ const descriptor = Object.getOwnPropertyDescriptor(Type.prototype, key);
+ if (descriptor && typeof descriptor.get === 'function') {
+ return obj;
+ }
const adapterType = typeof adapter[key];
const expectedType = typeof Type.prototype[key];
if (adapterType !== expectedType) {
diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js
index f08bface5a..c2da329358 100644
--- a/src/Controllers/DatabaseController.js
+++ b/src/Controllers/DatabaseController.js
@@ -499,6 +499,12 @@ class DatabaseController {
} catch (error) {
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
}
+ try {
+ const { validateFileUrlsInObject } = require('../FileUrlValidator');
+ validateFileUrlsInObject(update, this.options);
+ } catch (error) {
+ return Promise.reject(error instanceof Parse.Error ? error : new Parse.Error(Parse.Error.FILE_SAVE_ERROR, error.message || error));
+ }
const originalQuery = query;
const originalUpdate = update;
// Make a copy of the object, so we don't mutate the incoming data.
@@ -836,6 +842,12 @@ class DatabaseController {
} catch (error) {
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
}
+ try {
+ const { validateFileUrlsInObject } = require('../FileUrlValidator');
+ validateFileUrlsInObject(object, this.options);
+ } catch (error) {
+ return Promise.reject(error instanceof Parse.Error ? error : new Parse.Error(Parse.Error.FILE_SAVE_ERROR, error.message || error));
+ }
// Make a copy of the object, so we don't mutate the incoming data.
const originalObject = object;
object = transformObjectACL(object);
diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js
index a88c527b00..e21ea7c1a3 100644
--- a/src/Controllers/FilesController.js
+++ b/src/Controllers/FilesController.js
@@ -3,7 +3,7 @@ import { randomHexString } from '../cryptoUtils';
import AdaptableController from './AdaptableController';
import { validateFilename, FilesAdapter } from '../Adapters/Files/FilesAdapter';
import path from 'path';
-const Parse = require('parse').Parse;
+const Parse = require('parse/node').Parse;
const legacyFilesRegex = new RegExp(
'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*'
@@ -29,6 +29,16 @@ export class FilesController extends AdaptableController {
filename = randomHexString(32) + '_' + filename;
}
+ // Fallback: buffer stream for adapters that don't support streaming
+ if (typeof data?.pipe === 'function' && !this.adapter.supportsStreaming) {
+ data = await new Promise((resolve, reject) => {
+ const chunks = [];
+ data.on('data', chunk => chunks.push(chunk));
+ data.on('end', () => resolve(Buffer.concat(chunks)));
+ data.on('error', reject);
+ });
+ }
+
const location = await this.adapter.getFileLocation(config, filename);
await this.adapter.createFile(filename, data, contentType, options);
return {
diff --git a/src/Deprecator/Deprecations.js b/src/Deprecator/Deprecations.js
index cd47d80c4e..0b04902500 100644
--- a/src/Deprecator/Deprecations.js
+++ b/src/Deprecator/Deprecations.js
@@ -15,4 +15,10 @@
*
* If there are no deprecations, this must return an empty array.
*/
-module.exports = [];
+module.exports = [
+ {
+ optionKey: 'fileUpload.allowedFileUrlDomains',
+ changeNewDefault: '[]',
+ solution: "Set 'fileUpload.allowedFileUrlDomains' to the domains you want to allow, or to '[]' to block all file URLs.",
+ },
+];
diff --git a/src/FileUrlValidator.js b/src/FileUrlValidator.js
new file mode 100644
index 0000000000..6554fea51c
--- /dev/null
+++ b/src/FileUrlValidator.js
@@ -0,0 +1,68 @@
+const Parse = require('parse/node').Parse;
+
+/**
+ * Validates whether a File URL is allowed based on the configured allowed domains.
+ * @param {string} fileUrl - The URL to validate.
+ * @param {Object} config - The Parse Server config object.
+ * @throws {Parse.Error} If the URL is not allowed.
+ */
+function validateFileUrl(fileUrl, config) {
+ if (fileUrl == null || fileUrl === '') {
+ return;
+ }
+
+ const domains = config?.fileUpload?.allowedFileUrlDomains;
+ if (!Array.isArray(domains) || domains.includes('*')) {
+ return;
+ }
+
+ let parsedUrl;
+ try {
+ parsedUrl = new URL(fileUrl);
+ } catch {
+ throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `Invalid file URL.`);
+ }
+
+ const fileHostname = parsedUrl.hostname.toLowerCase();
+ for (const domain of domains) {
+ const d = domain.toLowerCase();
+ if (fileHostname === d) {
+ return;
+ }
+ if (d.startsWith('*.') && fileHostname.endsWith(d.slice(1))) {
+ return;
+ }
+ }
+
+ throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File URL domain '${parsedUrl.hostname}' is not allowed.`);
+}
+
+/**
+ * Recursively scans an object for File type fields and validates their URLs.
+ * @param {any} obj - The object to scan.
+ * @param {Object} config - The Parse Server config object.
+ * @throws {Parse.Error} If any File URL is not allowed.
+ */
+function validateFileUrlsInObject(obj, config) {
+ if (obj == null || typeof obj !== 'object') {
+ return;
+ }
+ if (Array.isArray(obj)) {
+ for (const item of obj) {
+ validateFileUrlsInObject(item, config);
+ }
+ return;
+ }
+ if (obj.__type === 'File' && obj.url) {
+ validateFileUrl(obj.url, config);
+ return;
+ }
+ for (const key of Object.keys(obj)) {
+ const value = obj[key];
+ if (value && typeof value === 'object') {
+ validateFileUrlsInObject(value, config);
+ }
+ }
+}
+
+module.exports = { validateFileUrl, validateFileUrlsInObject };
diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js
index 833ec93294..a879dcbdc2 100644
--- a/src/GraphQL/transformers/mutation.js
+++ b/src/GraphQL/transformers/mutation.js
@@ -97,6 +97,10 @@ const transformers = {
const { fileInfo } = await handleUpload(upload, config);
return { ...fileInfo, __type: 'File' };
} else if (file && file.name) {
+ if (file.url) {
+ const { validateFileUrl } = require('../../FileUrlValidator');
+ validateFileUrl(file.url, config);
+ }
return { name: file.name, __type: 'File', url: file.url };
}
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.');
diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js
index 973e8b301b..7a3c5043df 100644
--- a/src/Options/Definitions.js
+++ b/src/Options/Definitions.js
@@ -15,37 +15,32 @@ module.exports.SchemaOptions = {
},
definitions: {
env: 'PARSE_SERVER_SCHEMA_DEFINITIONS',
- help:
- 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema',
+ help: 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema',
required: true,
action: parsers.objectParser,
default: [],
},
deleteExtraFields: {
env: 'PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS',
- help:
- 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.',
+ help: 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.',
action: parsers.booleanParser,
default: false,
},
keepUnknownIndexes: {
env: 'PARSE_SERVER_SCHEMA_KEEP_UNKNOWN_INDEXES',
- help:
- "(Optional) Keep indexes that are present in the database but not defined in the schema. Set this to `true` if you are adding indexes manually, so that they won't be removed when running schema migration. Default is `false`.",
+ help: "(Optional) Keep indexes that are present in the database but not defined in the schema. Set this to `true` if you are adding indexes manually, so that they won't be removed when running schema migration. Default is `false`.",
action: parsers.booleanParser,
default: false,
},
lockSchemas: {
env: 'PARSE_SERVER_SCHEMA_LOCK_SCHEMAS',
- help:
- 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.',
+ help: 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.',
action: parsers.booleanParser,
default: false,
},
recreateModifiedFields: {
env: 'PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS',
- help:
- 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.',
+ help: 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.',
action: parsers.booleanParser,
default: false,
},
@@ -77,8 +72,7 @@ module.exports.ParseServerOptions = {
},
allowExpiredAuthDataToken: {
env: 'PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN',
- help:
- 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.',
+ help: 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.',
action: parsers.booleanParser,
default: false,
},
@@ -89,8 +83,7 @@ module.exports.ParseServerOptions = {
},
allowOrigin: {
env: 'PARSE_SERVER_ALLOW_ORIGIN',
- help:
- 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.',
+ help: 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.',
action: parsers.arrayParser,
},
analyticsAdapter: {
@@ -109,8 +102,7 @@ module.exports.ParseServerOptions = {
},
auth: {
env: 'PARSE_SERVER_AUTH_PROVIDERS',
- help:
- 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication',
+ help: 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication',
action: parsers.objectParser,
},
cacheAdapter: {
@@ -150,15 +142,13 @@ module.exports.ParseServerOptions = {
},
convertEmailToLowercase: {
env: 'PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE',
- help:
- 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.',
+ help: 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
convertUsernameToLowercase: {
env: 'PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE',
- help:
- 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.',
+ help: 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
@@ -171,8 +161,7 @@ module.exports.ParseServerOptions = {
},
databaseAdapter: {
env: 'PARSE_SERVER_DATABASE_ADAPTER',
- help:
- 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.',
+ help: 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.',
action: parsers.moduleOrObjectParser,
},
databaseOptions: {
@@ -195,8 +184,7 @@ module.exports.ParseServerOptions = {
},
directAccess: {
env: 'PARSE_SERVER_DIRECT_ACCESS',
- help:
- 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.
If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.
\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.',
+ help: 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.
If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.
\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.',
action: parsers.booleanParser,
default: true,
},
@@ -211,15 +199,13 @@ module.exports.ParseServerOptions = {
},
emailVerifyTokenReuseIfValid: {
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID',
- help:
- 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.
Default is `false`.
Requires option `verifyUserEmails: true`.',
+ help: 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.
Default is `false`.
Requires option `verifyUserEmails: true`.',
action: parsers.booleanParser,
default: false,
},
emailVerifyTokenValidityDuration: {
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION',
- help:
- 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.
For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).
Default is `undefined`.
Requires option `verifyUserEmails: true`.',
+ help: 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.
For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).
Default is `undefined`.
Requires option `verifyUserEmails: true`.',
action: parsers.numberParser('emailVerifyTokenValidityDuration'),
},
enableAnonymousUsers: {
@@ -230,8 +216,7 @@ module.exports.ParseServerOptions = {
},
enableCollationCaseComparison: {
env: 'PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON',
- help:
- 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.',
+ help: 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
@@ -243,15 +228,13 @@ module.exports.ParseServerOptions = {
},
enableInsecureAuthAdapters: {
env: 'PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS',
- help:
- 'Optional. Enables insecure authentication adapters. Insecure auth adapters are deprecated and will be removed in a future version. Defaults to `false`.',
+ help: 'Optional. Enables insecure authentication adapters. Insecure auth adapters are deprecated and will be removed in a future version. Defaults to `false`.',
action: parsers.booleanParser,
default: false,
},
enableSanitizedErrorResponse: {
env: 'PARSE_SERVER_ENABLE_SANITIZED_ERROR_RESPONSE',
- help:
- 'If set to `true`, error details are removed from error messages in responses to client requests, and instead a generic error message is sent. Default is `true`.',
+ help: 'If set to `true`, error details are removed from error messages in responses to client requests, and instead a generic error message is sent. Default is `true`.',
action: parsers.booleanParser,
default: true,
},
@@ -267,15 +250,13 @@ module.exports.ParseServerOptions = {
},
expireInactiveSessions: {
env: 'PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS',
- help:
- 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.',
+ help: 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.',
action: parsers.booleanParser,
default: true,
},
extendSessionOnUse: {
env: 'PARSE_SERVER_EXTEND_SESSION_ON_USE',
- help:
- "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.",
+ help: "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.",
action: parsers.booleanParser,
default: false,
},
@@ -297,8 +278,7 @@ module.exports.ParseServerOptions = {
},
graphQLPath: {
env: 'PARSE_SERVER_GRAPHQL_PATH',
- help:
- 'The mount path for the GraphQL endpoint
\u26A0\uFE0F File upload inside the GraphQL mutation system requires Parse Server to be able to call itself by making requests to the URL set in `serverURL`.
Defaults is `/graphql`.',
+ help: 'The mount path for the GraphQL endpoint
\u26A0\uFE0F File upload inside the GraphQL mutation system requires Parse Server to be able to call itself by making requests to the URL set in `serverURL`.
Defaults is `/graphql`.',
default: '/graphql',
},
graphQLPublicIntrospection: {
@@ -318,8 +298,7 @@ module.exports.ParseServerOptions = {
},
idempotencyOptions: {
env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS',
- help:
- 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.',
+ help: 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.',
action: parsers.objectParser,
type: 'IdempotencyOptions',
default: {},
@@ -368,14 +347,12 @@ module.exports.ParseServerOptions = {
},
maintenanceKey: {
env: 'PARSE_SERVER_MAINTENANCE_KEY',
- help:
- '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.
\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.',
+ help: '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.
\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.',
required: true,
},
maintenanceKeyIps: {
env: 'PARSE_SERVER_MAINTENANCE_KEY_IPS',
- help:
- "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.
This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.
Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.
Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.
Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.",
+ help: "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.
This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.
Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.
Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.
Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.",
action: parsers.arrayParser,
default: ['127.0.0.1', '::1'],
},
@@ -386,15 +363,13 @@ module.exports.ParseServerOptions = {
},
masterKeyIps: {
env: 'PARSE_SERVER_MASTER_KEY_IPS',
- help:
- "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.
This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.
Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.
Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.
Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.",
+ help: "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.
This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.
Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.
Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.
Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.",
action: parsers.arrayParser,
default: ['127.0.0.1', '::1'],
},
masterKeyTtl: {
env: 'PARSE_SERVER_MASTER_KEY_TTL',
- help:
- '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.',
+ help: '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.',
action: parsers.numberParser('masterKeyTtl'),
},
maxLimit: {
@@ -404,8 +379,7 @@ module.exports.ParseServerOptions = {
},
maxLogFiles: {
env: 'PARSE_SERVER_MAX_LOG_FILES',
- help:
- "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)",
+ help: "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)",
action: parsers.numberOrStringParser('maxLogFiles'),
},
maxUploadSize: {
@@ -472,15 +446,13 @@ module.exports.ParseServerOptions = {
},
preventLoginWithUnverifiedEmail: {
env: 'PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL',
- help:
- 'Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.
Default is `false`.
Requires option `verifyUserEmails: true`.',
- action: parsers.booleanParser,
+ help: "Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required. Supports a function with a return value of `true` or `false` for conditional prevention. The function receives a request object that includes `createdWith` to indicate whether the invocation is for `signup` or `login` and the used auth provider.
The `createdWith` values per scenario: