From dc25e53a5764823a6af559c907eaf543c78cd82f Mon Sep 17 00:00:00 2001 From: philsch Date: Mon, 16 Feb 2026 16:54:49 +0100 Subject: [PATCH] feat: upgrade ajv from v6 to v8 Upgrade ajv from 6.12.6 to 8.18.0 and add ajv-formats companion package for format keyword support extracted from core in v8. Key changes: - Update import path from 'ajv/lib/ajv' to 'ajv' (v8 package structure) - Replace removed `nullable` constructor option with `strict: false` and convert schema-level `nullable: true` to standard `type: ['string', 'null']` - Add `unicodeRegExp: false` to avoid regex compilation errors with existing URI patterns in themes handler - Rewrite prompts customText schema to use propertyNames with enum constraints instead of enumerating 195k+ property nodes (81 languages x 36 prompts x 67 screens) which caused stack overflow in v8's code generator. - Update test assertions for v8 error message changes ("should" -> "must", minLength wording) --- package-lock.json | 145 ++++++++++++++++------ package.json | 5 +- src/tools/auth0/handlers/customDomains.ts | 3 +- src/tools/auth0/handlers/prompts.ts | 55 ++------ src/tools/auth0/index.ts | 6 +- test/tools/auth0/validator.tests.js | 20 +-- 6 files changed, 133 insertions(+), 101 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72851c0d1..04398e8e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "8.27.0", "license": "MIT", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.18.0", + "ajv-formats": "^3.0.1", "auth0": "^5.3.1", "dot-prop": "^5.3.0", "fs-extra": "^10.1.0", @@ -490,6 +491,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -524,6 +542,13 @@ "node": ">= 4" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1244,21 +1269,38 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -2670,6 +2712,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2704,6 +2763,13 @@ "node": ">= 4" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2842,6 +2908,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -2851,6 +2918,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -4219,9 +4302,9 @@ "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -5635,6 +5718,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5743,6 +5827,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -6620,35 +6713,6 @@ "node": ">=0.3.1" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6870,6 +6934,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" diff --git a/package.json b/package.json index 0877d3ffd..6afdd841c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "readme": "README.md", "homepage": "https://github.com/auth0/auth0-deploy-cli#readme", "dependencies": { - "ajv": "^6.12.6", + "ajv": "^8.18.0", + "ajv-formats": "^3.0.1", "auth0": "^5.3.1", "dot-prop": "^5.3.0", "fs-extra": "^10.1.0", @@ -47,11 +48,11 @@ "yargs": "^15.4.1" }, "devDependencies": { + "@eslint/js": "^9.39.2", "@types/fs-extra": "^9.0.13", "@types/lodash": "^4.17.23", "@types/mocha": "^10.0.10", "@types/nconf": "^0.10.7", - "@eslint/js": "^9.39.2", "@typescript-eslint/eslint-plugin": "^8.55.0", "@typescript-eslint/parser": "^8.55.0", "chai": "^4.5.0", diff --git a/src/tools/auth0/handlers/customDomains.ts b/src/tools/auth0/handlers/customDomains.ts index 43d204b5d..40179496f 100644 --- a/src/tools/auth0/handlers/customDomains.ts +++ b/src/tools/auth0/handlers/customDomains.ts @@ -10,8 +10,7 @@ export const schema = { properties: { custom_domain_id: { type: 'string' }, custom_client_ip_header: { - type: 'string', - nullable: true, + type: ['string', 'null'], enum: ['true-client-ip', 'cf-connecting-ip', 'x-forwarded-for', null], }, domain: { type: 'string' }, diff --git a/src/tools/auth0/handlers/prompts.ts b/src/tools/auth0/handlers/prompts.ts index 57d9f94f2..32bd930b7 100644 --- a/src/tools/auth0/handlers/prompts.ts +++ b/src/tools/auth0/handlers/prompts.ts @@ -194,33 +194,16 @@ export const schema = { }, customText: { type: 'object', - properties: languages.reduce( - (acc, language) => ({ - ...acc, - [language]: { - type: 'object', - properties: promptTypes.reduce( - (promptAcc, promptType) => ({ - ...promptAcc, - [promptType]: { - type: 'object', - properties: screenTypes.reduce( - (screenAcc, screenType) => ({ - ...screenAcc, - [screenType]: { - type: 'object', - }, - }), - {} - ), - }, - }), - {} - ), - }, - }), - {} - ), + propertyNames: { enum: [...languages] }, + additionalProperties: { + type: 'object', + propertyNames: { enum: [...promptTypes] }, + additionalProperties: { + type: 'object', + propertyNames: { enum: [...screenTypes] }, + additionalProperties: { type: 'object' }, + }, + }, }, partials: { type: 'object', @@ -264,24 +247,6 @@ export const schema = { }, screenRenderers: { type: 'array', - properties: promptTypes.reduce( - (promptAcc, promptType) => ({ - ...promptAcc, - [promptType]: { - type: 'array', - properties: screenTypes.reduce( - (screenAcc, screenType) => ({ - ...screenAcc, - [screenType]: { - type: 'string', - }, - }), - {} - ), - }, - }), - {} - ), }, }, }; diff --git a/src/tools/auth0/index.ts b/src/tools/auth0/index.ts index a192dee92..25b704683 100644 --- a/src/tools/auth0/index.ts +++ b/src/tools/auth0/index.ts @@ -1,4 +1,5 @@ -import Ajv from 'ajv/lib/ajv'; +import Ajv from 'ajv'; +import addFormats from 'ajv-formats'; import pagedClient from './client'; import schema from './schema'; @@ -95,7 +96,8 @@ export default class Auth0 { } async validate(): Promise { - const ajv = new Ajv({ useDefaults: true, nullable: true }); + const ajv = new Ajv({ useDefaults: true, strict: false, unicodeRegExp: false }); + addFormats(ajv); const nonNullAssets = Object.keys(this.assets) .filter((k) => this.assets[k] != null) .reduce((a, k) => ({ ...a, [k]: this.assets[k] }), {}); diff --git a/test/tools/auth0/validator.tests.js b/test/tools/auth0/validator.tests.js index 3c7ef4541..1f1105b81 100644 --- a/test/tools/auth0/validator.tests.js +++ b/test/tools/auth0/validator.tests.js @@ -60,7 +60,7 @@ describe('#schema validation tests', () => { auth0 .validate() - .then(failedCb(done), passedCb(done, `should have required property '${field}'`)); + .then(failedCb(done), passedCb(done, `must have required property '${field}'`)); }; const checkEnum = (data, done) => { @@ -78,7 +78,7 @@ describe('#schema validation tests', () => { auth0 .validate() - .then(failedCb(done), passedCb(done, 'should be equal to one of the allowed values')); + .then(failedCb(done), passedCb(done, 'must be equal to one of the allowed values')); }; const checkTypeError = (field, expectedType, data, done) => { @@ -94,7 +94,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, `should be ${expectedType}`, field)); + auth0.validate().then(failedCb(done), passedCb(done, `must be ${expectedType}`, field)); }; describe('#branding validate', () => { @@ -117,7 +117,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); + auth0.validate().then(failedCb(done), passedCb(done, 'must be object')); }); it('should pass validation', (done) => { @@ -172,7 +172,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, 'should be array')); + auth0.validate().then(failedCb(done), passedCb(done, 'must be array')); }); it('should pass validation', (done) => { @@ -220,7 +220,7 @@ describe('#schema validation tests', () => { auth0 .validate() - .then(failedCb(done), passedCb(done, 'should NOT be shorter than 1 characters')); + .then(failedCb(done), passedCb(done, 'must NOT have fewer than 1 characters')); }); it('should pass validation', (done) => { @@ -321,7 +321,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); + auth0.validate().then(failedCb(done), passedCb(done, 'must be object')); }); it('should pass validation', (done) => { @@ -620,7 +620,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); + auth0.validate().then(failedCb(done), passedCb(done, 'must be object')); }); it('should pass validation', (done) => { @@ -760,7 +760,7 @@ describe('#schema validation tests', () => { mockConfigFn ); - auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); + auth0.validate().then(failedCb(done), passedCb(done, 'must be object')); }); it('should pass validation', (done) => { @@ -783,7 +783,7 @@ describe('#schema validation tests', () => { auth0 .validate() - .then(failedCb(done), passedCb(done, "should have required property 'borders'")); + .then(failedCb(done), passedCb(done, "must have required property 'borders'")); }); it('should pass validation', (done) => {