From 60ef7255d78c4f922b8d3efdc8cd4518a54ffaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Mon, 5 May 2025 14:46:56 -0300 Subject: [PATCH 01/34] feat: upgrade unenv 1.x to 2.x (#159) * feat: update unenv to version 2.0.0-rc.15 * feat: add support for require in esbuild when production * chore: update package-lock.json * feat: add crypto polyfills to dev runtime and defining with external * fix: prepend 'node:' to non-external module paths in nodeBuiltInModules * refactor: remove unused handlePublicDir function and related logic * fix: update MEM_FILES declaration and improve path prefix handling in fixMappedFilesPaths function * feat: added stream imports to runtime dev * fix: enhance body parsing in handleFetchEvent for various Content-Types * feat: add promises context and related polyfills to bundler * docs: add deprecation notice for webpack bundler and update related documentation * refactor: remove deprecated stream polyfills and clean up related code * feat: add assert-browserify dependency and update polyfills mapping --- package-lock.json | 361 +++++++++++++++--- package.json | 4 +- packages/bundler/package.json | 2 +- .../src/bundlers/esbuild/esbuild.config.ts | 2 + .../bundler/src/bundlers/esbuild/esbuild.ts | 13 +- .../plugins/node-polyfills/node-polyfills.ts | 23 +- .../plugins/node-polyfills/node-polyfills.ts | 12 +- .../crypto/context/crypto.context.js | 68 ++++ .../src/polyfills/crypto/context/index.js | 3 + .../src/polyfills/crypto/crypto.polyfills.js | 73 ++++ .../bundler/src/polyfills/crypto/index.js | 7 + .../src/polyfills/fs/context/fs.context.js | 2 + .../bundler/src/polyfills/fs/fs.polyfills.js | 41 +- packages/bundler/src/polyfills/index.d.ts | 3 + packages/bundler/src/polyfills/index.js | 6 + .../src/polyfills/promises/context/index.js | 3 + .../promises/context/promises.context.js | 13 + .../bundler/src/polyfills/promises/index.js | 7 + .../src/polyfills/stream/context/index.js | 3 + .../stream/context/stream.context.js | 21 + .../bundler/src/polyfills/stream/index.js | 7 + .../src/polyfills/stream/stream.polyfills.js | 104 +++++ packages/config/README.md | 84 ++-- .../node/custom-server/12.3.x/server/next.js | 23 ++ .../src/presets/next/node/prebuild/index.js | 21 - packages/unenv-preset/src/index.ts | 18 +- .../unenv-preset/src/polyfills/node/crypto.js | 17 + .../unenv-preset/src/polyfills/node/empty.js | 3 + .../unenv-preset/src/polyfills/node/fs.js | 24 +- .../src/polyfills/node/globals/process.cjs | 26 +- .../unenv-preset/src/polyfills/node/https.js | 196 ++++++++++ .../unenv-preset/src/polyfills/node/module.js | 41 +- 32 files changed, 1035 insertions(+), 196 deletions(-) create mode 100644 packages/bundler/src/polyfills/crypto/context/crypto.context.js create mode 100644 packages/bundler/src/polyfills/crypto/context/index.js create mode 100644 packages/bundler/src/polyfills/crypto/crypto.polyfills.js create mode 100644 packages/bundler/src/polyfills/crypto/index.js create mode 100644 packages/bundler/src/polyfills/promises/context/index.js create mode 100644 packages/bundler/src/polyfills/promises/context/promises.context.js create mode 100644 packages/bundler/src/polyfills/promises/index.js create mode 100644 packages/bundler/src/polyfills/stream/context/index.js create mode 100644 packages/bundler/src/polyfills/stream/context/stream.context.js create mode 100644 packages/bundler/src/polyfills/stream/index.js create mode 100644 packages/bundler/src/polyfills/stream/stream.polyfills.js create mode 100644 packages/unenv-preset/src/polyfills/node/empty.js create mode 100644 packages/unenv-preset/src/polyfills/node/https.js diff --git a/package-lock.json b/package-lock.json index d98379ee..84963ea7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "azion", - "version": "1.19.0", + "version": "1.19.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "azion", - "version": "1.19.0", + "version": "1.19.1", "license": "MIT", "workspaces": [ "packages/*" @@ -21,6 +21,7 @@ "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-keywords": "^5.1.0", + "assert-browserify": "^2.0.0", "babel-loader": "^9.2.1", "browserify-zlib": "^0.2.0", "chalk": "^5.3.0", @@ -39,20 +40,15 @@ "pcre-to-regexp": "^1.1.0", "progress": "^2.0.3", "signale": "^1.4.0", - "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", "timers-browserify": "^2.0.12", - "unenv": "^1.10.0", + "unenv": "^2.0.0-rc.15", "url": "^0.11.4", "util": "^0.12.5", "vm-browserify": "^1.1.2", "webpack": "^5.97.1" }, - "bin": { - "azion": "bin/azion", - "azlib": "bin/azion" - }, "devDependencies": { "@commitlint/cli": "^18.4.1", "@commitlint/config-conventional": "^18.4.0", @@ -5818,6 +5814,18 @@ "node": ">=12.0.0" } }, + "node_modules/assert-browserify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert-browserify/-/assert-browserify-2.0.0.tgz", + "integrity": "sha512-SJvtrHmyaOT57oKWIpzWZr2hLkFyXjg5ajNT+RHvd9fhpruhrJF0OYT0yy8rIgvSn3xQp/VpLQAOwO0KNVKrJw==", + "license": "MIT", + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -6097,6 +6105,32 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -6220,6 +6254,27 @@ "node": ">= 0.12" } }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/browserify-sign/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6239,6 +6294,21 @@ } ] }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", @@ -6766,6 +6836,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", + "dev": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -6888,7 +6959,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "8.3.6", @@ -7383,6 +7455,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/defu": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", @@ -7514,6 +7603,32 @@ "readable-stream": "^2.0.2" } }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7773,6 +7888,12 @@ "node": ">= 0.4" } }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", @@ -8140,6 +8261,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.4.tgz", + "integrity": "sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8524,6 +8651,32 @@ "readable-stream": "^2.0.0" } }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -8671,6 +8824,22 @@ "traverse": "0.6.8" } }, + "node_modules/git-log-parser/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/git-log-parser/node_modules/split2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", @@ -8680,6 +8849,16 @@ "through2": "~2.0.0" } }, + "node_modules/git-log-parser/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/git-log-parser/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -9319,6 +9498,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-natural-number": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", @@ -9429,7 +9624,8 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", @@ -11355,11 +11551,6 @@ "lodash": "^4.17.21" } }, - "node_modules/node-fetch-native": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", - "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==" - }, "node_modules/node-inspect-extracted": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/node-inspect-extracted/-/node-inspect-extracted-1.1.0.tgz", @@ -14730,6 +14921,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15091,9 +15313,10 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" }, "node_modules/pbkdf2": { "version": "3.1.2", @@ -15423,7 +15646,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -15835,28 +16059,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -16936,6 +17138,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -16964,10 +17167,37 @@ "readable-stream": "^2.0.2" } }, + "node_modules/stream-combiner2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-combiner2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/stream-http": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "license": "MIT", "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -17259,6 +17489,32 @@ "node": ">= 0.8.0" } }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/temp-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", @@ -17925,6 +18181,12 @@ } } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "license": "MIT" + }, "node_modules/uglify-js": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", @@ -17978,15 +18240,16 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/unenv": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz", - "integrity": "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==", + "version": "2.0.0-rc.15", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.15.tgz", + "integrity": "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==", + "license": "MIT", "dependencies": { - "consola": "^3.2.3", "defu": "^6.1.4", - "mime": "^3.0.0", - "node-fetch-native": "^1.6.4", - "pathe": "^1.1.2" + "exsolve": "^1.0.4", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "ufo": "^1.5.4" } }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -18542,7 +18805,7 @@ "string_decoder": "^1.3.0", "timers-browserify": "^2.0.12", "ts-loader": "^9.5.2", - "unenv": "^1.10.0", + "unenv": "^2.0.0-rc.15", "url": "^0.11.4", "util": "^0.12.5", "vm-browserify": "^1.1.2", diff --git a/package.json b/package.json index 2f3e72b2..6af78a64 100644 --- a/package.json +++ b/package.json @@ -231,6 +231,7 @@ "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-keywords": "^5.1.0", + "assert-browserify": "^2.0.0", "babel-loader": "^9.2.1", "browserify-zlib": "^0.2.0", "chalk": "^5.3.0", @@ -249,11 +250,10 @@ "pcre-to-regexp": "^1.1.0", "progress": "^2.0.3", "signale": "^1.4.0", - "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", "timers-browserify": "^2.0.12", - "unenv": "^1.10.0", + "unenv": "^2.0.0-rc.15", "url": "^0.11.4", "util": "^0.12.5", "vm-browserify": "^1.1.2", diff --git a/packages/bundler/package.json b/packages/bundler/package.json index 62b892c4..bceb6300 100644 --- a/packages/bundler/package.json +++ b/packages/bundler/package.json @@ -46,7 +46,7 @@ "string_decoder": "^1.3.0", "timers-browserify": "^2.0.12", "ts-loader": "^9.5.2", - "unenv": "^1.10.0", + "unenv": "^2.0.0-rc.15", "url": "^0.11.4", "util": "^0.12.5", "vm-browserify": "^1.1.2", diff --git a/packages/bundler/src/bundlers/esbuild/esbuild.config.ts b/packages/bundler/src/bundlers/esbuild/esbuild.config.ts index 61e233a9..5c03279e 100644 --- a/packages/bundler/src/bundlers/esbuild/esbuild.config.ts +++ b/packages/bundler/src/bundlers/esbuild/esbuild.config.ts @@ -6,6 +6,8 @@ export default { platform: 'browser', mainFields: ['browser', 'module', 'main'], target: 'es2022', + keepNames: true, + allowOverwrite: true, loader: { '.js': 'js', }, diff --git a/packages/bundler/src/bundlers/esbuild/esbuild.ts b/packages/bundler/src/bundlers/esbuild/esbuild.ts index 68cb16a4..9dad3384 100644 --- a/packages/bundler/src/bundlers/esbuild/esbuild.ts +++ b/packages/bundler/src/bundlers/esbuild/esbuild.ts @@ -32,12 +32,19 @@ const bundlerPlugins = createBundlerPlugins + (config: ESBuildConfig, ctx: BuildContext) => (content: string | undefined): ESBuildConfig => { if (!content) return config; - config.banner = config.banner || {}; + config.banner.js = config.banner.js ? `${config.banner.js} ${content}` : content; + if (ctx.production) { + config.banner.js += ` +// This file is generated by Azion. Do not edit it manually. +// Dynamic require is not supported in production mode. +import { createRequire as _createRequire } from "node:module"; +const require = _createRequire(import.meta.url);`; + } return config; }; @@ -66,7 +73,7 @@ export const createAzionESBuildConfig = (buildConfig: BuildConfiguration, ctx: B flow([ () => bundlerPlugins.applyPolyfills(ctx)(config)(buildConfig), () => bundlerPlugins.applyAzionModule(ctx)(config), - () => applyContentInjection(config)(buildConfig?.setup?.contentToInject), + () => applyContentInjection(config, ctx)(buildConfig?.setup?.contentToInject), () => applyDefineVars(config, 'esbuild')(buildConfig?.setup?.defineVars), () => extendConfig(config)(buildConfig.extend as (config: ESBuildConfiguration) => ESBuildConfiguration), ])(config), diff --git a/packages/bundler/src/bundlers/esbuild/plugins/node-polyfills/node-polyfills.ts b/packages/bundler/src/bundlers/esbuild/plugins/node-polyfills/node-polyfills.ts index eac748b5..49164a76 100644 --- a/packages/bundler/src/bundlers/esbuild/plugins/node-polyfills/node-polyfills.ts +++ b/packages/bundler/src/bundlers/esbuild/plugins/node-polyfills/node-polyfills.ts @@ -5,12 +5,20 @@ import fs from 'fs'; import { createRequire } from 'module'; import { builtinModules } from 'node:module'; import path from 'path'; -import { env, nodeless } from 'unenv'; +import { defineEnv } from 'unenv'; import helper from './helper/index'; const requireCustom = createRequire(import.meta.url); -const { alias, inject, polyfill, external } = env(nodeless, unenvPresetAzion); +const { env } = defineEnv({ + nodeCompat: true, + resolve: false, + overrides: { + ...unenvPresetAzion, + }, +}); + +const { alias, inject, polyfill, external } = env; interface BuildOptions { define?: Record; @@ -21,14 +29,12 @@ interface GlobalInjectResult { exportName: string; } -type GlobalInjectValue = string | string[] | [string, string]; - /** * Get global inject * @param {*} globalInject Global inject * @returns {*} Return import statement and export name */ -function getGlobalInject(globalInject: GlobalInjectValue): GlobalInjectResult { +function getGlobalInject(globalInject: string | string[]): GlobalInjectResult { if (typeof globalInject === 'string') { return { importStatement: `import globalVar from "${globalInject}";`, @@ -128,7 +134,7 @@ function nodeBuiltInModules( // if polyfill is not found, check if the module is external if (!polyfillResult && externalModule) { return { - path: args.path, + path: args.path.startsWith('node:') ? args.path : `node:${args.path}`, external: externalModule.includes(args.path), }; } @@ -186,7 +192,8 @@ function handleNodeJSGlobals(build: PluginBuild, getAbsolutePath: (moving: strin if (!match?.[1]) throw new Error(`Invalid global name: ${args.path}`); const globalName = match[1]; - const { importStatement, exportName } = getGlobalInject(inject[globalName]); + + const { importStatement, exportName } = getGlobalInject(inject[globalName] as string | string[]); return { contents: ` @@ -259,7 +266,7 @@ function defineNextJsRuntime(options: BuildOptions) { // eslint-disable-next-line no-param-reassign options.define = { ...options.define, - 'process.env.NEXT_RUNTIME': '"edge"', + 'process.env.NEXT_RUNTIME': options.define?.['process.env.NEXT_RUNTIME'] || '"edge"', 'process.env.NEXT_COMPUTE_JS': 'true', 'process.env.__NEXT_BUILD_ID': `"${buildId}"`, }; diff --git a/packages/bundler/src/bundlers/webpack/plugins/node-polyfills/node-polyfills.ts b/packages/bundler/src/bundlers/webpack/plugins/node-polyfills/node-polyfills.ts index f36a200c..dff9c656 100644 --- a/packages/bundler/src/bundlers/webpack/plugins/node-polyfills/node-polyfills.ts +++ b/packages/bundler/src/bundlers/webpack/plugins/node-polyfills/node-polyfills.ts @@ -3,12 +3,20 @@ import { getAbsoluteDirPath } from 'azion/utils/node'; import fs from 'fs'; import { createRequire } from 'module'; import path from 'path'; -import { env, nodeless } from 'unenv'; +import { defineEnv } from 'unenv'; import { Compiler, WebpackPluginInstance } from 'webpack'; const require = createRequire(import.meta.url); -const { alias, inject, polyfill, external } = env(nodeless, unenvPresetAzion); +const { env } = defineEnv({ + nodeCompat: true, + resolve: false, + overrides: { + ...unenvPresetAzion, + }, +}); + +const { alias, inject, polyfill, external } = env; class NodePolyfillPlugin implements WebpackPluginInstance { private INTERNAL_POLYFILL_PATH = '/polyfills'; diff --git a/packages/bundler/src/polyfills/crypto/context/crypto.context.js b/packages/bundler/src/polyfills/crypto/context/crypto.context.js new file mode 100644 index 00000000..1e023541 --- /dev/null +++ b/packages/bundler/src/polyfills/crypto/context/crypto.context.js @@ -0,0 +1,68 @@ +/* eslint-disable */ +import * as crypto from 'node:crypto'; + +export var { Cipher } = crypto; +export var { Decipher } = crypto; +export var { DiffieHellman } = crypto; +export var { DiffieHellmanGroup } = crypto; +export var { Hash } = crypto; +export var { Hmac } = crypto; +export var { Sign } = crypto; +export var { Verify } = crypto; +export var { constants } = crypto; +export var { createCipheriv } = crypto; +export var { createDecipheriv } = crypto; +export var { createDiffieHellman } = crypto; +export var { createDiffieHellmanGroup } = crypto; +export var { createECDH } = crypto; +export var { createHash } = crypto; +export var { createHmac } = crypto; +export var { createSign } = crypto; +export var { createVerify } = crypto; +export var { getCiphers } = crypto; +export var { getDiffieHellman } = crypto; +export var { getHashes } = crypto; +export var { pbkdf2 } = crypto; +export var { pbkdf2Sync } = crypto; +export var { privateDecrypt } = crypto; +export var { privateEncrypt } = crypto; +export var { pseudoRandomBytes } = crypto; +export var { publicDecrypt } = crypto; +export var { publicEncrypt } = crypto; +export var { randomBytes } = crypto; +export var { randomFill } = crypto; +export var { randomFillSync } = crypto; + +export default { + Cipher, + Decipher, + DiffieHellman, + DiffieHellmanGroup, + Hash, + Hmac, + Sign, + Verify, + constants, + createCipheriv, + createDecipheriv, + createDiffieHellman, + createDiffieHellmanGroup, + createECDH, + createHash, + createHmac, + createSign, + createVerify, + getCiphers, + getDiffieHellman, + getHashes, + pbkdf2, + pbkdf2Sync, + privateDecrypt, + privateEncrypt, + pseudoRandomBytes, + publicDecrypt, + publicEncrypt, + randomBytes, + randomFill, + randomFillSync, +}; diff --git a/packages/bundler/src/polyfills/crypto/context/index.js b/packages/bundler/src/polyfills/crypto/context/index.js new file mode 100644 index 00000000..b25ab622 --- /dev/null +++ b/packages/bundler/src/polyfills/crypto/context/index.js @@ -0,0 +1,3 @@ +import cryptoContext from './crypto.context.js'; + +export default cryptoContext; diff --git a/packages/bundler/src/polyfills/crypto/crypto.polyfills.js b/packages/bundler/src/polyfills/crypto/crypto.polyfills.js new file mode 100644 index 00000000..53215496 --- /dev/null +++ b/packages/bundler/src/polyfills/crypto/crypto.polyfills.js @@ -0,0 +1,73 @@ +/* eslint-disable */ +/** This polyfill is referenced in #build/bundlers/polyfills/polyfills-manager.js + * + * CRYPTO_CONTEXT is defined in runtime.env.js for use on the local server + */ + +/* eslint-disable */ + +export var { Cipher } = CRYPTO_CONTEXT.cryptoContext; +export var { Decipher } = CRYPTO_CONTEXT.cryptoContext; +export var { DiffieHellman } = CRYPTO_CONTEXT.cryptoContext; +export var { DiffieHellmanGroup } = CRYPTO_CONTEXT.cryptoContext; +export var { Hash } = CRYPTO_CONTEXT.cryptoContext; +export var { Hmac } = CRYPTO_CONTEXT.cryptoContext; +export var { Sign } = CRYPTO_CONTEXT.cryptoContext; +export var { Verify } = CRYPTO_CONTEXT.cryptoContext; +export var { constants } = CRYPTO_CONTEXT.cryptoContext; +export var { createCipheriv } = CRYPTO_CONTEXT.cryptoContext; +export var { createDecipheriv } = CRYPTO_CONTEXT.cryptoContext; +export var { createDiffieHellman } = CRYPTO_CONTEXT.cryptoContext; +export var { createDiffieHellmanGroup } = CRYPTO_CONTEXT.cryptoContext; +export var { createECDH } = CRYPTO_CONTEXT.cryptoContext; +export var { createHash } = CRYPTO_CONTEXT.cryptoContext; +export var { createHmac } = CRYPTO_CONTEXT.cryptoContext; +export var { createSign } = CRYPTO_CONTEXT.cryptoContext; +export var { createVerify } = CRYPTO_CONTEXT.cryptoContext; +export var { getCiphers } = CRYPTO_CONTEXT.cryptoContext; +export var { getDiffieHellman } = CRYPTO_CONTEXT.cryptoContext; +export var { getHashes } = CRYPTO_CONTEXT.cryptoContext; +export var { pbkdf2 } = CRYPTO_CONTEXT.cryptoContext; +export var { pbkdf2Sync } = CRYPTO_CONTEXT.cryptoContext; +export var { privateDecrypt } = CRYPTO_CONTEXT.cryptoContext; +export var { privateEncrypt } = CRYPTO_CONTEXT.cryptoContext; +export var { pseudoRandomBytes } = CRYPTO_CONTEXT.cryptoContext; +export var { publicDecrypt } = CRYPTO_CONTEXT.cryptoContext; +export var { publicEncrypt } = CRYPTO_CONTEXT.cryptoContext; +export var { randomBytes } = CRYPTO_CONTEXT.cryptoContext; +export var { randomFill } = CRYPTO_CONTEXT.cryptoContext; +export var { randomFillSync } = CRYPTO_CONTEXT.cryptoContext; + +export default { + Cipher, + Decipher, + DiffieHellman, + DiffieHellmanGroup, + Hash, + Hmac, + Sign, + Verify, + constants, + createCipheriv, + createDecipheriv, + createDiffieHellman, + createDiffieHellmanGroup, + createECDH, + createHash, + createHmac, + createSign, + createVerify, + getCiphers, + getDiffieHellman, + getHashes, + pbkdf2, + pbkdf2Sync, + privateDecrypt, + privateEncrypt, + pseudoRandomBytes, + publicDecrypt, + publicEncrypt, + randomBytes, + randomFill, + randomFillSync, +}; diff --git a/packages/bundler/src/polyfills/crypto/index.js b/packages/bundler/src/polyfills/crypto/index.js new file mode 100644 index 00000000..5c6a2584 --- /dev/null +++ b/packages/bundler/src/polyfills/crypto/index.js @@ -0,0 +1,7 @@ +/** + * We are not exporting the crypto.polyfill.js from this structure due to the context definition in runtime.env.js. + * As we are proxying the Node.js crypto lib, it is not possible to export the crypto.polyfill.js file. + */ +import cryptoContext from './context/index.js'; + +export default { cryptoContext }; diff --git a/packages/bundler/src/polyfills/fs/context/fs.context.js b/packages/bundler/src/polyfills/fs/context/fs.context.js index d5d0afe6..641eb49e 100644 --- a/packages/bundler/src/polyfills/fs/context/fs.context.js +++ b/packages/bundler/src/polyfills/fs/context/fs.context.js @@ -9,6 +9,7 @@ export const { openSync, close, closeSync, + existsSync, stat, statSync, lstat, @@ -55,6 +56,7 @@ localFs.open = open; localFs.openSync = openSync; localFs.close = close; localFs.closeSync = closeSync; +localFs.existsSync = existsSync; localFs.stat = stat; localFs.statSync = statSync; localFs.lstat = lstat; diff --git a/packages/bundler/src/polyfills/fs/fs.polyfills.js b/packages/bundler/src/polyfills/fs/fs.polyfills.js index b990b379..1f171332 100644 --- a/packages/bundler/src/polyfills/fs/fs.polyfills.js +++ b/packages/bundler/src/polyfills/fs/fs.polyfills.js @@ -24,6 +24,11 @@ function closeSync(...args) { return FS_CONTEXT.closeSync(...args); } +function existsSync(path) { + path = join(BUILD_PATH_PREFIX, path); + return FS_CONTEXT.existsSync(path); +} + function stat(path, ...args) { path = join(BUILD_PATH_PREFIX, path); return FS_CONTEXT.stat(path, ...args); @@ -157,28 +162,16 @@ const W_OK = 2; const X_OK = 1; export { - promises, - open, - openSync, close, closeSync, - stat, - statSync, - lstat, - lstatSync, - readFile, - readFileSync, - readdir, - readdirSync, - mkdir, - rmdir, + constants, copyFile, cp, - writeFile, - rename, - realpath, - constants, + existsSync, F_OK, + lstat, + lstatSync, + mkdir, O_APPEND, O_CREAT, O_DIRECTORY, @@ -193,8 +186,21 @@ export { O_SYNC, O_TRUNC, O_WRONLY, + open, + openSync, + promises, R_OK, + readdir, + readdirSync, + readFile, + readFileSync, + realpath, + rename, + rmdir, + stat, + statSync, W_OK, + writeFile, X_OK, }; @@ -204,6 +210,7 @@ localFs.open = open; localFs.openSync = openSync; localFs.close = close; localFs.closeSync = closeSync; +localFs.existsSync = existsSync; localFs.stat = stat; localFs.statSync = statSync; localFs.lstat = lstat; diff --git a/packages/bundler/src/polyfills/index.d.ts b/packages/bundler/src/polyfills/index.d.ts index 778f22d8..7b43519e 100644 --- a/packages/bundler/src/polyfills/index.d.ts +++ b/packages/bundler/src/polyfills/index.d.ts @@ -11,4 +11,7 @@ declare module 'azion/bundler/polyfills' { export const NetworkListContext: any; export const fsContext: any; export const FirewallEventContext: any; + export const streamContext: any; + export const cryptoContext: any; + export const promisesContext: any; } diff --git a/packages/bundler/src/polyfills/index.js b/packages/bundler/src/polyfills/index.js index 733d853d..26131e71 100644 --- a/packages/bundler/src/polyfills/index.js +++ b/packages/bundler/src/polyfills/index.js @@ -5,17 +5,23 @@ import fetchContext from './azion/fetch/index.js'; import FirewallEventContext from './azion/firewall-event/index.js'; import NetworkListContext from './azion/network-list/index.js'; import { StorageContext } from './azion/storage/index.js'; +import cryptoContext from './crypto/index.js'; import { fsContext } from './fs/index.js'; +import promisesContext from './promises/index.js'; +import { streamContext } from './stream/index.js'; // TODO: transform polyfills to TypeScript export { AsyncHooksContext, + cryptoContext, EnvVarsContext, fetchContext, FetchEventContext, FirewallEventContext, fsContext, NetworkListContext, + promisesContext, StorageContext, + streamContext, }; diff --git a/packages/bundler/src/polyfills/promises/context/index.js b/packages/bundler/src/polyfills/promises/context/index.js new file mode 100644 index 00000000..4c68a3f6 --- /dev/null +++ b/packages/bundler/src/polyfills/promises/context/index.js @@ -0,0 +1,3 @@ +import promisesContext from './promises.context.js'; + +export default promisesContext; diff --git a/packages/bundler/src/polyfills/promises/context/promises.context.js b/packages/bundler/src/polyfills/promises/context/promises.context.js new file mode 100644 index 00000000..b8a8e0df --- /dev/null +++ b/packages/bundler/src/polyfills/promises/context/promises.context.js @@ -0,0 +1,13 @@ +globalThis.Promise = Promise || globalThis.Promise; + +globalThis.Promise.withResolvers = function () { + let resolve; + let reject; + const promise = new globalThis.Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve: resolve, reject: reject }; +}; + +export default globalThis.Promise.withResolvers; diff --git a/packages/bundler/src/polyfills/promises/index.js b/packages/bundler/src/polyfills/promises/index.js new file mode 100644 index 00000000..df1d92fd --- /dev/null +++ b/packages/bundler/src/polyfills/promises/index.js @@ -0,0 +1,7 @@ +/** + * We are not exporting the promises.polyfill.js from this structure due to the context definition in runtime.env.js. + * As we are proxying the Node.js promises lib, it is not possible to export the promises.polyfill.js file. + */ +import promisesContext from './context/index.js'; + +export default { promisesContext }; diff --git a/packages/bundler/src/polyfills/stream/context/index.js b/packages/bundler/src/polyfills/stream/context/index.js new file mode 100644 index 00000000..d453fb08 --- /dev/null +++ b/packages/bundler/src/polyfills/stream/context/index.js @@ -0,0 +1,3 @@ +import streamContext from './stream.context.js'; + +export default streamContext; diff --git a/packages/bundler/src/polyfills/stream/context/stream.context.js b/packages/bundler/src/polyfills/stream/context/stream.context.js new file mode 100644 index 00000000..4f9dec57 --- /dev/null +++ b/packages/bundler/src/polyfills/stream/context/stream.context.js @@ -0,0 +1,21 @@ +/* eslint-disable */ +import stream from 'node:stream'; + +export var { Duplex } = stream; +export var { Writable } = stream; +export var { Readable } = stream; +export var { Transform } = stream; +export var { PassThrough } = stream; +export var { Stream } = stream; +export var { prototype } = stream; + +export default { + Duplex, + Writable, + Readable, + Transform, + PassThrough, + Stream, + stream, + prototype, +}; diff --git a/packages/bundler/src/polyfills/stream/index.js b/packages/bundler/src/polyfills/stream/index.js new file mode 100644 index 00000000..1be5acd7 --- /dev/null +++ b/packages/bundler/src/polyfills/stream/index.js @@ -0,0 +1,7 @@ +/** + * We are not exporting the async_hooks.polyfill.js from this structure due to the context definition in runtime.env.js. + * As we are proxying the Node.js async_hooks lib, it is not possible to export the async_hooks.polyfill.js file. + */ +import streamContext from './context/index.js'; + +export { streamContext }; diff --git a/packages/bundler/src/polyfills/stream/stream.polyfills.js b/packages/bundler/src/polyfills/stream/stream.polyfills.js new file mode 100644 index 00000000..f91de692 --- /dev/null +++ b/packages/bundler/src/polyfills/stream/stream.polyfills.js @@ -0,0 +1,104 @@ +/* eslint-disable */ +/** This polyfill is referenced in #build/bundlers/polyfills/polyfills-manager.js + * + * STREAM_CONTEXT is defined in runtime.env.js for use on the local server + */ + +export var { Duplex } = STREAM_CONTEXT; +export var { Writable } = STREAM_CONTEXT; +export var { Readable } = STREAM_CONTEXT; +export var { Transform } = STREAM_CONTEXT; +export var { PassThrough } = STREAM_CONTEXT; +export var { Stream } = STREAM_CONTEXT; +export var { prototype } = STREAM_CONTEXT; + +Readable.toWeb = function (readable) { + const stream = new ReadableStream({ + start(controller) { + readable.on('data', (chunk) => { + controller.enqueue(chunk); + }); + readable.on('end', () => { + controller.close(); + }); + readable.on('error', (error) => { + controller.error(error); + }); + }, + }); + return stream; +}; + +Readable.fromWeb = function (webStream) { + const reader = webStream.getReader(); + return new Readable({ + async read(size) { + const { done, value } = await reader.read(); + if (done) { + return { done: true }; + } + return { done: false, value }; + }, + async destroy(error) { + await reader.cancel(error); + }, + }); +}; + +Writable.toWeb = function (webStream) { + const writer = webStream.getWriter(); + + writer.closed.catch((error) => { + console.error('WritableStream closed with error:', error); + console.error('Error details:', error?.message, error?.stack); + }); + + return new Writable({ + write(chunk, encoding, callback) { + if (writer.desiredSize === null) { + const err = new Error('WritableStream is not writable or has been closed.'); + console.error(err.message); + callback(err); + return; + } + + writer + .write(chunk) + .then(() => callback(undefined)) + .catch((err) => { + callback(err); + }); + }, + final(callback) { + writer.ready + .then(() => { + return writer.close(); + }) + .then(() => callback(undefined)) + .catch((err) => { + callback(err); + }); + }, + destroy(error, callback) { + writer + .abort(error) + .then(() => { + callback(undefined); + }) + .catch((err) => { + callback(err); + }); + }, + }); +}; + +export default { + Duplex, + Writable, + Readable, + Transform, + PassThrough, + Stream, + stream: STREAM_CONTEXT, + prototype, +}; diff --git a/packages/config/README.md b/packages/config/README.md index 4561312d..49213a8b 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -304,15 +304,18 @@ Converts a Azion JSON configuration object to a AzionConfig object. Type definition for the build configuration. +> ⚠️ \*Deprecation Notice: +> Support for the webpack bundler will be discontinued in future releases. While it is still available for now, new features, fixes, and improvements will be focused exclusively on esbuild. We recommend migrating to esbuild as soon as possible to ensure compatibility and better performance in upcoming versions. + **Properties:** -- `bundler?: 'esbuild' | 'webpack'` - O empacotador a ser usado. -- `preset?: string | AzionBuildPreset` - O preset a ser usado, pode ser uma string ou um objeto AzionBuildPreset. -- `entry?: string | string[] | Record` - O arquivo de entrada, pode ser uma string, array de strings ou um objeto. -- `polyfills?: boolean` - Se deve incluir polyfills. -- `worker?: boolean` - Se deve construir um worker. -- `extend?: (context: T) => T` - Função para estender a configuração do bundler. -- `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - Configuração do sistema de arquivos em memória. +- \*`bundler?: 'esbuild' | 'webpack'` - The bundler to be used. Default is 'esbuild'. +- `preset?: string | AzionBuildPreset` - The preset to be used, can be a string or an AzionBuildPreset object. +- `entry?: string | string[] | Record` - The entry file, can be a string, an array of strings, or an object. +- `polyfills?: boolean` - Whether to include polyfills. +- `worker?: boolean` - Whether to build a worker. +- `extend?: (context: T) => T` - Function to extend the bundler configuration. +- `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - In-memory file system configuration. ### `AzionBuildPreset` @@ -320,14 +323,14 @@ Type definition for the build preset. **Properties:** -- `config: AzionConfig` - A configuração do Azion. -- `handler?: (event: FetchEvent) => Promise` - Manipulador de eventos opcional. -- `prebuild?: (config: BuildConfiguration, ctx: BuildContext) => Promise` - Hook executado antes da construção. -- `postbuild?: (config: BuildConfiguration, ctx: BuildContext) => Promise` - Hook executado após a construção. -- `metadata: PresetMetadata` - Metadados do preset. - - `name: string` - Nome do preset. - - `registry?: string` - Registro do preset. - - `ext?: string` - Extensão do arquivo. +- `config: AzionConfig` - The Azion configuration. +- `handler?: (event: FetchEvent) => Promise` - Optional event handler. +- `prebuild?: (config: BuildConfiguration, ctx: BuildContext) => Promise` - Hook executed before the build process. +- `postbuild?: (config: BuildConfiguration, ctx: BuildContext) => Promise` - Hook executed after the build process. +- `metadata: PresetMetadata` - Preset metadata. + - `name: string` - Preset name. + - `registry?: string` - Preset registry. + - `ext?: string` - File extension. ### `AzionPrebuildResult` @@ -335,14 +338,14 @@ Type definition for the prebuild result. **Properties:** -- `filesToInject: string[]` - Arquivos a serem injetados na memória durante o processo de construção. -- `injection: object` - Configurações de injeção de código. - - `globals: object` - Variáveis globais a serem injetadas. - - `entry?: string` - Código no início do worker. - - `banner?: string` - Código no topo do worker. -- `bundler: object` - Configurações do empacotador. - - `defineVars: object` - Variáveis a serem definidas. - - `plugins: (EsbuildPlugin | WebpackPlugin)[]` - Plugins a serem usados. +- `filesToInject: string[]` - Files to be injected into memory during the build process. +- `injection: object` - Code injection settings. + - `globals: object` - Global variables to be injected. + - `entry?: string` - Code to run at the start of the worker. + - `banner?: string` - Code to place at the top of the worker. +- `bundler: object` - Bundler configuration. + - `defineVars: object` - Variables to be defined. + - `plugins: (EsbuildPlugin | WebpackPlugin)[]` - Plugins to be used. ### `BuildContext` @@ -350,24 +353,27 @@ Type definition for the build context. **Properties:** -- `production: boolean` - Se está em modo de produção. -- `handler: BuildEntryPoint` - O ponto de entrada da construção. +- `production: boolean` - Whether it is in production mode. +- `handler: BuildEntryPoint` - The build entry point. ### `BuildConfiguration` Type definition for the build configuration. +> ⚠️ \*Deprecation Notice: +> Support for the webpack bundler will be discontinued in future releases. While it is still available for now, new features, fixes, and improvements will be focused exclusively on esbuild. We recommend migrating to esbuild as soon as possible to ensure compatibility and better performance in upcoming versions. + **Properties:** -- `entry: Record` - Os pontos de entrada. -- `baseOutputDir?: string` - Diretório base de saída. -- `preset: AzionBuildPreset` - O preset a ser usado. -- `setup: BundlerSetup` - Configuração do empacotador. -- `bundler?: 'webpack' | 'esbuild'` - O empacotador a ser usado. -- `polyfills?: boolean` - Se deve incluir polyfills. -- `worker?: boolean` - Se deve construir um worker. -- `extend?: (context: T) => T` - Função para estender a configuração do bundler. -- `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - Configuração do sistema de arquivos em memória. +- `entry: Record` - The entry points. +- `baseOutputDir?: string` - Base output directory. +- `preset: AzionBuildPreset` - The preset to be used. +- `setup: BundlerSetup` - Bundler configuration. +- \*`bundler?: 'webpack' | 'esbuild'` - The bundler to be used. +- `polyfills?: boolean` - Whether to include polyfills. +- `worker?: boolean` - Whether to build a worker. +- `extend?: (context: T) => T` - Function to extend the bundler configuration. +- `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - In-memory file system configuration. ### `BundlerSetup` @@ -375,8 +381,8 @@ Type definition for the bundler setup. **Properties:** -- `contentToInject?: string` - Conteúdo a ser injetado. -- `defineVars?: Record` - Variáveis a serem definidas. +- `contentToInject?: string` - Content to be injected. +- `defineVars?: Record` - Variables to be defined. ### `PresetMetadata` @@ -384,9 +390,9 @@ Type definition for the preset metadata. **Properties:** -- `name: string` - Nome do preset. -- `registry?: string` - Registro do preset. -- `ext?: string` - Extensão do arquivo. +- `name: string` - Preset name. +- `registry?: string` - Preset registry. +- `ext?: string` - File extension. ### `AzionDomain` diff --git a/packages/presets/src/presets/next/node/custom-server/12.3.x/server/next.js b/packages/presets/src/presets/next/node/custom-server/12.3.x/server/next.js index 1624a85c..dc2b1175 100644 --- a/packages/presets/src/presets/next/node/custom-server/12.3.x/server/next.js +++ b/packages/presets/src/presets/next/node/custom-server/12.3.x/server/next.js @@ -56,6 +56,29 @@ export class NextServer { async handleFetchEvent(event) { const { req, res } = toReqRes(event.request); + // Consume the ReadableStream (req._stream) and populate req.body + if (req._stream) { + // Determine Content-Type + const contentType = req.headers['content-type'] || req.headers['Content-Type']; + + if (contentType === 'application/json') { + try { + req.body = await event.request.json(); // Parse JSON if Content-Type is application/json + } catch (error) { + console.error('Invalid JSON body:', error); + req.body = null; // Handle invalid JSON gracefully + } + } else if (contentType === 'application/x-www-form-urlencoded') { + req.body = new URLSearchParams(await event.request.text()).toString(); // Parse URL-encoded data + } else if (contentType === 'text/plain' || !contentType) { + req.body = await event.request.text(); // Handle plain text or no Content-Type + } else { + console.warn(`Unhandled Content-Type: ${contentType}`); + req.body = event.request.body; // Default fallback: raw string + } + } else { + req.body = JSON.stringify({}); // Handle cases where there is no body + } const nextRequest = new ComputeJsNextRequest(req, event.client); const nextResponse = new ComputeJsNextResponse(res); diff --git a/packages/presets/src/presets/next/node/prebuild/index.js b/packages/presets/src/presets/next/node/prebuild/index.js index e5493417..23b838f7 100644 --- a/packages/presets/src/presets/next/node/prebuild/index.js +++ b/packages/presets/src/presets/next/node/prebuild/index.js @@ -4,21 +4,6 @@ import path, { join } from 'path'; import { copyDirectory, feedback, getAbsoluteDirPath } from 'azion/utils/node'; import BuildStatic from './statics/index.js'; -/** - * If a relative path exists, copy public path to root - * @param {string} pathPrefix - prefix - * @param {string} rootDir - application root dir - */ -function handlePublicDir(pathPrefix, rootDir) { - const validPathPrefix = pathPrefix && typeof pathPrefix === 'string' && pathPrefix !== ''; - - if (validPathPrefix) { - const srcPublicDir = path.resolve(pathPrefix, 'public'); - const destPublicDir = path.resolve(rootDir, 'public'); - copyDirectory(srcPublicDir, destPublicDir); - } -} - /** * Run actions to build next for node runtime. * @param {string} nextVersion - project next version in package.json @@ -47,12 +32,6 @@ async function run(nextVersion, buildContext) { process.exit(1); } - // STATICS - // copy to root public dir if necessary - if (buildContext.memoryFS) { - handlePublicDir(buildContext.memoryFS.removePathPrefix, rootDir); - } - // It is necessary to replace the static directories for the .vercel output, // which has the _next pattern and the public folder does not exist // as the files are in the root (.vercel/output/static). diff --git a/packages/unenv-preset/src/index.ts b/packages/unenv-preset/src/index.ts index dac95cde..69ccad17 100644 --- a/packages/unenv-preset/src/index.ts +++ b/packages/unenv-preset/src/index.ts @@ -5,7 +5,6 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const getAbsolutePath = () => path.resolve(__dirname, '../../', 'unenv-preset', 'src'); -const nextNodePresetPath = `${getAbsolutePath()}/polyfills/node/frameworks/next`; const polyfillsPath = `${getAbsolutePath()}/polyfills`; export default { @@ -14,33 +13,26 @@ export default { __filename: `${polyfillsPath}/node/globals/path-filename.js`, process: `${polyfillsPath}/node/globals/process.cjs`, performance: `${polyfillsPath}/node/globals/performance.js`, - navigator: `${polyfillsPath}/node/globals/navigator.js`, }, alias: { 'azion/utils': 'azion/utils', 'azion/utils/edge': 'azion/utils/edge', 'azion/utils/node': 'azion/utils/node', '@fastly/http-compute-js': '@fastly/http-compute-js', - 'next/dist/compiled/etag': `${nextNodePresetPath}/custom-server/12.3.x/util/etag.js`, accepts: 'accepts', - crypto: `${polyfillsPath}/node/crypto.js`, - events: 'events/events.js', - http: 'stream-http', + assert: 'assert-browserify', + https: `${polyfillsPath}/node/https.js`, module: `${polyfillsPath}/node/module.js`, - stream: 'stream-browserify/', string_decoder: 'string_decoder/lib/string_decoder.js', - url: 'url/url.js', - util: 'util/util.js', timers: 'timers-browserify/', - inherits: 'inherits/inherits_browser.js', - vm: 'vm-browserify/', - zlib: 'browserify-zlib', }, - external: ['node:async_hooks', 'node:fs/promises'], + external: ['node:async_hooks', 'node:fs/promises', 'node:stream', 'node:crypto'], polyfill: [ 'aziondev:async_hooks:/async-hooks/async-hooks.polyfills.js', 'aziondev:fs:/fs/fs.polyfills.js', 'aziondev:fs/promises:/fs/promises/promises.polyfills.js', + 'aziondev:stream:/stream/stream.polyfills.js', + 'aziondev:crypto:/crypto/crypto.polyfills.js', `azionprd:fs:/fs.js`, ], }; diff --git a/packages/unenv-preset/src/polyfills/node/crypto.js b/packages/unenv-preset/src/polyfills/node/crypto.js index ade2fe3e..dfb0d273 100644 --- a/packages/unenv-preset/src/polyfills/node/crypto.js +++ b/packages/unenv-preset/src/polyfills/node/crypto.js @@ -65,3 +65,20 @@ export var randomUUID = function () { return (c ^ (getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16); }); }; + +export var generateKeyPair = function (type, options, callback) { + if (typeof options === 'function') { + callback = options; + options = undefined; + } + if (typeof type === 'function') { + callback = type; + type = undefined; + } + + if (type === 'rsa') { + return crypto.generateKeyPair('rsa', options, callback); + } else { + throw new Error('Unsupported key type'); + } +}; diff --git a/packages/unenv-preset/src/polyfills/node/empty.js b/packages/unenv-preset/src/polyfills/node/empty.js new file mode 100644 index 00000000..89f4afba --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/empty.js @@ -0,0 +1,3 @@ +export function loadEnvConfig() {} + +export default {}; diff --git a/packages/unenv-preset/src/polyfills/node/fs.js b/packages/unenv-preset/src/polyfills/node/fs.js index 6e97dc17..14a70956 100644 --- a/packages/unenv-preset/src/polyfills/node/fs.js +++ b/packages/unenv-preset/src/polyfills/node/fs.js @@ -4,7 +4,7 @@ import bPath from 'path'; /* eslint-disable */ -const MEM_FILES = globalThis.bundler.__FILES__; +var MEM_FILES = globalThis.bundler.__FILES__; globalThis.bundler.FS_PATHS_CHANGED = false; @@ -13,12 +13,13 @@ globalThis.bundler.FS_PATHS_CHANGED = false; */ function fixMappedFilesPaths() { const prefix = globalThis.bundler.FS_PATH_PREFIX_TO_REMOVE; - if (!globalThis.bundler.FS_PATHS_CHANGED && prefix !== '') { + if (!globalThis.bundler.FS_PATHS_CHANGED && (prefix !== undefined || prefix !== '""')) { + let CHANGED_PATHS = {}; Object.keys(MEM_FILES).forEach((e) => { const newKey = e.replace(prefix, ''); - MEM_FILES[newKey] = MEM_FILES[e]; - delete MEM_FILES[e]; + CHANGED_PATHS[newKey] = MEM_FILES[e]; }); + MEM_FILES = CHANGED_PATHS; } globalThis.bundler.FS_PATHS_CHANGED = true; @@ -381,6 +382,17 @@ function readdirSync(path, options = {}) { return result; } +function existsSync(path) { + path = getValidatedPath(path); + const filesInfos = getFilesInfos(); + + if (filesInfos.paths.includes(path)) { + return true; + } + + return false; +} + // Use Cells node:fs API const fsPolyfill = Object.create(SRC_NODE_FS); fsPolyfill.close = close; @@ -390,10 +402,11 @@ fsPolyfill.statSync = statSync; fsPolyfill.lstatSync = statSync; fsPolyfill.readFileSync = readFileSync; fsPolyfill.readdirSync = readdirSync; +fsPolyfill.existsSync = existsSync; export default fsPolyfill; -export { close, closeSync, statSync as lstatSync, openSync, readdirSync, readFileSync, statSync }; +export { close, closeSync, existsSync, statSync as lstatSync, openSync, readdirSync, readFileSync, statSync }; export const { access, @@ -414,7 +427,6 @@ export const { Dir, Dirent, exists, - existsSync, F_OK, fdatasync, fdatasyncSync, diff --git a/packages/unenv-preset/src/polyfills/node/globals/process.cjs b/packages/unenv-preset/src/polyfills/node/globals/process.cjs index 5a691ee3..6e6e524e 100644 --- a/packages/unenv-preset/src/polyfills/node/globals/process.cjs +++ b/packages/unenv-preset/src/polyfills/node/globals/process.cjs @@ -154,8 +154,25 @@ Item.prototype.run = function () { }; processShim.title = 'browser'; processShim.browser = true; -processShim.env = processShim.env = - typeof globalThis.process !== 'undefined' && globalThis.process.env ? globalThis.process.env : {}; +const initialValues = globalThis.process !== 'undefined' && globalThis.process?.env ? globalThis.process.env : {}; +Object.defineProperty(processShim, 'env', { + value: new Proxy( + { ...initialValues }, + { + get(target, prop) { + return target[prop] || undefined; + }, + set(target, prop, value) { + target[prop] = value; + return true; + }, + }, + ), + writable: true, + enumerable: true, + configurable: true, +}); + processShim.argv = []; processShim.version = ''; // empty string to avoid regexp issues processShim.versions = { node: '18.3.1' }; @@ -185,11 +202,12 @@ processShim.cwd = function () { }; processShim.chdir = function (dir) { - throw new Error('process.chdir is not supported'); + return dir ?? '/'; }; processShim.umask = function () { return 0; }; globalThis.process = processShim; -globalThis.process.env = processShim.env; + +module.exports = processShim; diff --git a/packages/unenv-preset/src/polyfills/node/https.js b/packages/unenv-preset/src/polyfills/node/https.js new file mode 100644 index 00000000..bc7f2ed8 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/https.js @@ -0,0 +1,196 @@ +// https://nodejs.org/api/https.html +import { EventEmitter } from 'node:events'; + +function notImplementedClass(name) { + return class { + __unenv__ = true; + constructor() { + throw new Error(`[unenv] ${name} is not implemented yet!`); + } + }; +} +function createNotImplementedError(name) { + return new Error(`[unenv] ${name} is not implemented yet!`); +} + +function notImplemented(name) { + const fn = () => { + throw createNotImplementedError(name); + }; + return Object.assign(fn, { __unenv__: true }); +} + +class HttpAgent extends EventEmitter { + __unenv__ = {}; + maxFreeSockets = 256; + maxSockets = Infinity; + maxTotalSockets = Infinity; + freeSockets = {}; + sockets = {}; + requests = {}; + destroy() {} +} + +export const Server = notImplementedClass('https.Server'); + +export const Agent = HttpAgent; + +export const globalAgent = new Agent(); + +export const get = notImplemented('https.get'); + +export const createServer = notImplemented('https.createServer'); + +export const request = (options, callback) => { + let fullUrl; + let headers = {}; + let method; + + if (typeof options === 'object') { + const protocol = options.protocol || 'https:'; + const hostname = options.hostname || 'localhost'; + const port = options.port ? `:${options.port}` : ''; + const path = options.path || '/'; + method = options.method || 'GET'; + + // Adiciona os query params, se existirem + const queryParams = options.query ? new URLSearchParams(options.query).toString() : ''; + const queryString = queryParams ? `?${queryParams}` : ''; + + fullUrl = `${protocol}//${hostname}${port}${path}${queryString}`; + headers = { ...options.headers }; + } else { + fullUrl = options; + headers = options.headers || {}; + } + + const { body } = options; + + // Verifica o tipo de conteúdo e formata o corpo adequadamente + let formattedBody; + if (headers['Content-Type'] === 'application/json') { + formattedBody = body ? JSON.stringify(body) : undefined; + } else if (headers['Content-Type'] === 'application/x-www-form-urlencoded') { + formattedBody = body ? new URLSearchParams(body).toString() : undefined; + } else { + formattedBody = body; // Envia o corpo como está para outros tipos de conteúdo + } + + const fetchOptions = { + method, + headers, + body: JSON.stringify(formattedBody) || JSON.stringify({}), + }; + + const controller = new AbortController(); + const signal = controller.signal; + + let timeoutId; + if (options.timeout) { + timeoutId = setTimeout(() => { + controller.abort(); + reqEvents.emit('error', new Error('Request timed out')); + }, 5000); + } + + const reqEvents = new EventEmitter(); + + let endCalled = false; + + const req = { + _bodyBuffer: [], + write: (chunk) => { + if (typeof chunk === 'string' || chunk instanceof Buffer) { + req._bodyBuffer.push(chunk); // Adiciona o chunk ao buffer + } else { + throw new Error('Invalid chunk type. Expected string or Buffer.'); + } + }, + on: (event, listener) => { + reqEvents.on(event, listener); + return req; + }, + once: (event, listener) => { + reqEvents.once(event, listener); + return req; + }, + abort: () => { + clearTimeout(timeoutId); + controller.abort(); + }, + end: () => { + if (endCalled) { + console.error('end() called multiple times'); + return; + } + endCalled = true; + + // Concatena os chunks do buffer para formar o corpo completo + const body = req._bodyBuffer.length > 0 ? req._bodyBuffer.join('') : undefined; + + fetch(fullUrl, { ...fetchOptions, body, signal }) + .then(async (response) => { + clearTimeout(timeoutId); + + const res = new EventEmitter(); + res.statusCode = response.status; + res.headers = Object.fromEntries(response.headers.entries()); + + const reader = response.body?.getReader(); + + const processStream = async () => { + try { + if (!reader) { + console.log('No reader available'); + res.emit('end'); // Emite 'end' se não houver corpo na resposta + return; + } + // eslint-disable-next-line no-constant-condition + while (true) { + const { done, value } = await reader.read(); + if (done) { + res.emit('end'); + break; + } + res.emit('data', Buffer.from(value)); + } + } catch (err) { + console.error('Error processing stream:', err); + res.emit('error', err); + } + }; + + if (reader) { + if (!reader.locked) { + processStream(); + } else { + res.emit('error', new Error('Stream is already locked')); + } + } else { + res.emit('end'); + } + + reqEvents.emit('response', res); + + if (callback) { + callback(res); + } + }) + .catch((err) => { + clearTimeout(timeoutId); + reqEvents.emit('error', err); + }); + }, + }; + + return req; +}; + +export default { + Server, + Agent, + globalAgent, + get, + createServer, + request, +}; diff --git a/packages/unenv-preset/src/polyfills/node/module.js b/packages/unenv-preset/src/polyfills/node/module.js index 31569dce..506d7038 100644 --- a/packages/unenv-preset/src/polyfills/node/module.js +++ b/packages/unenv-preset/src/polyfills/node/module.js @@ -8,7 +8,7 @@ function unimplemented() { throw new Error('Not implemented yet!'); } -var builtinModules = [ +const builtinModules = [ '_http_agent', '_http_client', '_http_common', @@ -84,11 +84,8 @@ function _nodeModulePaths(...args) { /* EMPTY */ } -function _resolveFilename(...args) { - /* EMPTY */ -} - -export default { +// Crie um objeto para exportação +const moduleExports = { builtinModules: builtinModules, _cache: null, _pathCache: null, @@ -99,7 +96,7 @@ export default { _nodeModulePaths: _nodeModulePaths, _resolveLookupPaths: unimplemented, _load: _load, - _resolveFilename: _resolveFilename, + _resolveFilename: unimplemented, createRequireFromPath: unimplemented, createRequire: createRequire, _initPaths: unimplemented, @@ -111,28 +108,10 @@ export default { SourceMap: unimplemented, }; -export var _cache = null, - _pathCache = null, - _extensions = null, - globalPaths = null; - -export { - builtinModules, - unimplemented as _debug, - unimplemented as _findPath, - unimplemented as _nodeModulePaths, - unimplemented as _resolveLookupPaths, - unimplemented as _load, - unimplemented as _resolveFilename, - createRequire as createRequireFromPath, - createRequire as createRequire, - unimplemented as _initPaths, - unimplemented as _preloadModules, - unimplemented as syncBuiltinESMExports, - unimplemented as Module, - unimplemented as runMain, - unimplemented as findSourceMap, - unimplemented as SourceMap, -}; +Object.defineProperty(moduleExports, '_resolveFilename', { + value: unimplemented, + writable: true, + configurable: true, +}); -/* eslint-enable */ +export default moduleExports; From de7fd9226ffdf6b099dcb61757207401eb59d382 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 5 May 2025 17:48:15 +0000 Subject: [PATCH 02/34] chore(release): 1.20.0-stage.1 [skip ci] ## [1.20.0-stage.1](https://github.com/aziontech/lib/compare/v1.19.1...v1.20.0-stage.1) (2025-05-05) ### Features * upgrade unenv 1.x to 2.x (#159) ([60ef725](https://github.com/aziontech/lib/commit/60ef7255d78c4f922b8d3efdc8cd4518a54ffaff)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e11790d..8cb7d64d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.1](https://github.com/aziontech/lib/compare/v1.19.1...v1.20.0-stage.1) (2025-05-05) + + +### Features + +* upgrade unenv 1.x to 2.x (#159) ([60ef725](https://github.com/aziontech/lib/commit/60ef7255d78c4f922b8d3efdc8cd4518a54ffaff)) + ### [1.19.1](https://github.com/aziontech/lib/compare/v1.19.0...v1.19.1) (2025-04-22) diff --git a/package.json b/package.json index 6af78a64..75b25d6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.19.1", + "version": "1.20.0-stage.1", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From cd659dbd9afbbe5a7ed45e0d89f59e0fb76792d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Tue, 13 May 2025 14:06:43 -0300 Subject: [PATCH 03/34] fix: add Azion.Storage to globalThis for polyfill (#163) --- packages/bundler/src/helpers/azion-local-polyfills.ts | 1 + .../bundler/src/polyfills/azion/storage/storage.polyfills.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/bundler/src/helpers/azion-local-polyfills.ts b/packages/bundler/src/helpers/azion-local-polyfills.ts index 4f015522..d5dd149c 100644 --- a/packages/bundler/src/helpers/azion-local-polyfills.ts +++ b/packages/bundler/src/helpers/azion-local-polyfills.ts @@ -13,5 +13,6 @@ export default { ['azion:storage', `${externalPolyfillsPath}/storage/storage.polyfills.js`], ['Azion.env', `${externalPolyfillsPath}/env-vars/env-vars.polyfills.js`], ['Azion.networkList', `${externalPolyfillsPath}/network-list/network-list.polyfills.js`], + ['Azion.Storage', `${externalPolyfillsPath}/storage/storage.polyfills.js`], ]), }; diff --git a/packages/bundler/src/polyfills/azion/storage/storage.polyfills.js b/packages/bundler/src/polyfills/azion/storage/storage.polyfills.js index c1893280..d067959b 100644 --- a/packages/bundler/src/polyfills/azion/storage/storage.polyfills.js +++ b/packages/bundler/src/polyfills/azion/storage/storage.polyfills.js @@ -197,3 +197,6 @@ export class StorageObjectList { this.entries = list; } } + +globalThis.Azion = globalThis.Azion || {}; +globalThis.Azion.Storage = Storage; From 00a8ed20de49a55226da3c55c25310ac446ee646 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 13 May 2025 17:08:11 +0000 Subject: [PATCH 04/34] chore(release): 1.20.0-stage.2 [skip ci] ## [1.20.0-stage.2](https://github.com/aziontech/lib/compare/v1.20.0-stage.1...v1.20.0-stage.2) (2025-05-13) ### Bug Fixes * add Azion.Storage to globalThis for polyfill (#163) ([cd659db](https://github.com/aziontech/lib/commit/cd659dbd9afbbe5a7ed45e0d89f59e0fb76792d5)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb7d64d..bf1e0a01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.2](https://github.com/aziontech/lib/compare/v1.20.0-stage.1...v1.20.0-stage.2) (2025-05-13) + + +### Bug Fixes + +* add Azion.Storage to globalThis for polyfill (#163) ([cd659db](https://github.com/aziontech/lib/commit/cd659dbd9afbbe5a7ed45e0d89f59e0fb76792d5)) + ## [1.20.0-stage.1](https://github.com/aziontech/lib/compare/v1.19.1...v1.20.0-stage.1) (2025-05-05) diff --git a/package.json b/package.json index 75b25d6d..ab68151a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.1", + "version": "1.20.0-stage.2", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From a02d941c90360f1b8c10076ccb2b761f3ce62f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Thu, 15 May 2025 11:15:31 -0300 Subject: [PATCH 05/34] fix: wasm-image-processor resize wrapper (#164) * fix: wasm-image-processor resize wrapper * chore: rm typo --- packages/wasm-image-processor/src/index.ts | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/wasm-image-processor/src/index.ts b/packages/wasm-image-processor/src/index.ts index 54c170d5..8b71d9fa 100644 --- a/packages/wasm-image-processor/src/index.ts +++ b/packages/wasm-image-processor/src/index.ts @@ -13,7 +13,10 @@ function resize(image: photon.PhotonImage, width: number, height: number, usePer const widthPercent = usePercent ? width : (width * 100.0) / imageWidth; const heightPercent = usePercent ? height : (height * 100.0) / imageHeight; - return photon.resize(image, (imageWidth * widthPercent) / 100, (imageHeight * heightPercent) / 100, 1); + const newWidth = (imageWidth * widthPercent) / 100; + const newHeight = (imageHeight * heightPercent) / 100; + + return photon.resize(image, newWidth, newHeight, 1); } /** @@ -105,7 +108,9 @@ async function loadImage(pathOrURL: string): Promise { * Get the width of the image. * @returns {number} The width of the image in pixels. */ - width: (): number => image.get_width(), + width: (): number => { + return image.get_width(); + }, /** * Get the height of the image. * @returns {number} The height of the image in pixels. @@ -120,9 +125,26 @@ async function loadImage(pathOrURL: string): Promise { */ resize: (width: number, height: number, usePercent = true): WasmImage => { const resizedImage = resize(image, width, height, usePercent); + return { - ...wrapper, image: resizedImage, + width: (): number => resizedImage.get_width(), + height: (): number => resizedImage.get_height(), + resize: (w: number, h: number, up = true): WasmImage => { + const newResizedImage = resize(resizedImage, w, h, up); + return { + image: newResizedImage, + width: (): number => newResizedImage.get_width(), + height: (): number => newResizedImage.get_height(), + resize: wrapper.resize, + getImageResponse: (format: SupportedImageFormat, quality = 100.0): Response => + getImageResponse(newResizedImage, format, quality), + clean: () => clean(newResizedImage), + }; + }, + getImageResponse: (format: SupportedImageFormat, quality = 100.0): Response => + getImageResponse(resizedImage, format, quality), + clean: () => clean(resizedImage), }; }, /** From 720a7f3f368ccd710fda391e6a9ac1248ccc30cc Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 15 May 2025 14:16:49 +0000 Subject: [PATCH 06/34] chore(release): 1.20.0-stage.3 [skip ci] ## [1.20.0-stage.3](https://github.com/aziontech/lib/compare/v1.20.0-stage.2...v1.20.0-stage.3) (2025-05-15) ### Bug Fixes * wasm-image-processor resize wrapper (#164) ([a02d941](https://github.com/aziontech/lib/commit/a02d941c90360f1b8c10076ccb2b761f3ce62f2e)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1e0a01..f225581a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.3](https://github.com/aziontech/lib/compare/v1.20.0-stage.2...v1.20.0-stage.3) (2025-05-15) + + +### Bug Fixes + +* wasm-image-processor resize wrapper (#164) ([a02d941](https://github.com/aziontech/lib/commit/a02d941c90360f1b8c10076ccb2b761f3ce62f2e)) + ## [1.20.0-stage.2](https://github.com/aziontech/lib/compare/v1.20.0-stage.1...v1.20.0-stage.2) (2025-05-13) diff --git a/package.json b/package.json index ab68151a..727db5cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.2", + "version": "1.20.0-stage.3", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 49c3f02aa39d5e24eb2141db2370a26e63f8fe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 27 May 2025 18:05:16 -0300 Subject: [PATCH 07/34] feat(config): azion api v4 (#161) * feat: api v4 applications * feat: api v4 workloads * feat: api v4 edge connectors * feat: presets config api v4 * feat: applications api v4 * fix: revert firewall * fix: standardize property names * refactor: rm presets workload * fix: rm setOrigin behavior * feat: edge connector behavior * fix: use setEdgeConnector for presets rules * feat(iac): edge storage and build bindings * test: fix config tests * fix: preset metadata schema * fix: property name * fix: rm build bindings * fix: function binding type * fix: typo * fix: validate bindings * fix: workloads * feat: firewall config array * fix: use english for messages * fix: standardize names * fix: functions strategy * fix: schema validation feedback * refactor: presets config structure * fix: is_active property * refactor: rm order property * fix; edge application modules * fix: rm tls from addresses * feat: edge connector type and type_property * refactor: presets edge connector * chore: improve feedback * fix: rm domains * fix: rm unused type * fix: setEdgeConnector validation * refactor: presets rules abstraction * refactor: export presets with capital letters * fix: default entry for compute presets * fix: nextp reset config --- package-lock.json | 4 +- package.json | 10 + packages/config/package.json | 14 +- .../src/configProcessor/helpers/behaviors.ts | 29 +- .../helpers/convertLegacyConfig.ts | 68 - .../src/configProcessor/helpers/schema.ts | 1426 +++++++++++------ .../configProcessor/helpers/schemaManifest.ts | 888 +++++++--- .../processConfig/index.test.ts | 18 +- .../configProcessor/processConfig/index.ts | 4 +- .../cacheProcessConfigStrategy.ts | 54 +- .../edgeApplicationProcessConfigStrategy.ts | 73 + .../rulesProcessConfigStrategy.ts | 89 +- .../domainProcessConfigStrategy.ts | 82 - .../edgeConnectorProcessConfigStrategy.ts | 248 +++ .../functionsProcessConfigStrategy.ts | 73 +- .../localProcessConfigStrategy.ts | 33 - .../originProcessConfigStrategy.ts | 126 -- .../firewallProcessConfigStrategy.test.ts | 411 ++--- .../secure/firewallProcessConfigStrategy.ts | 174 +- .../storageProcessConfigStrategy.ts | 39 + .../workloadProcessConfigStrategy.ts | 106 ++ .../configProcessor/processStrategy/index.ts | 29 +- .../processStrategy/processConfigContext.ts | 2 +- .../processStrategy/processConfigStrategy.ts | 4 +- .../validateConfig/index.test.ts | 3 - packages/config/src/constants.ts | 39 +- packages/config/src/rules/constants.ts | 38 + packages/config/src/rules/index.ts | 2 + .../config/src/rules/request/createMPA.ts | 55 + .../config/src/rules/request/createSPA.ts | 47 + packages/config/src/types.ts | 299 +++- packages/config/tsconfig.json | 3 +- packages/config/tsup.config.ts | 13 + .../presets/src/presets/angular/config.ts | 66 +- packages/presets/src/presets/angular/index.ts | 2 +- packages/presets/src/presets/astro/config.ts | 72 +- packages/presets/src/presets/astro/index.ts | 2 +- .../presets/src/presets/docusaurus/config.ts | 72 +- .../presets/src/presets/docusaurus/index.ts | 2 +- .../presets/src/presets/eleventy/config.ts | 72 +- .../presets/src/presets/eleventy/index.ts | 2 +- .../presets/src/presets/emscripten/config.ts | 41 +- .../presets/src/presets/emscripten/index.ts | 2 +- packages/presets/src/presets/gatsby/config.ts | 74 +- packages/presets/src/presets/gatsby/index.ts | 2 +- packages/presets/src/presets/hexo/config.ts | 74 +- packages/presets/src/presets/hexo/index.ts | 2 +- packages/presets/src/presets/html/config.ts | 51 +- packages/presets/src/presets/html/index.ts | 2 +- packages/presets/src/presets/hugo/config.ts | 74 +- packages/presets/src/presets/hugo/index.ts | 2 +- .../presets/src/presets/javascript/config.ts | 44 +- .../presets/src/presets/javascript/index.ts | 2 +- packages/presets/src/presets/jekyll/config.ts | 74 +- packages/presets/src/presets/jekyll/index.ts | 2 +- packages/presets/src/presets/next/config.ts | 97 +- packages/presets/src/presets/next/index.ts | 2 +- packages/presets/src/presets/nuxt/config.ts | 76 +- packages/presets/src/presets/nuxt/index.ts | 2 +- packages/presets/src/presets/preact/config.ts | 68 +- packages/presets/src/presets/preact/index.ts | 2 +- packages/presets/src/presets/qwik/config.ts | 67 +- packages/presets/src/presets/qwik/index.ts | 2 +- packages/presets/src/presets/react/config.ts | 67 +- packages/presets/src/presets/react/index.ts | 2 +- .../presets/src/presets/rustwasm/config.ts | 43 +- .../presets/src/presets/rustwasm/index.ts | 2 +- .../presets/src/presets/stencil/config.ts | 67 +- packages/presets/src/presets/stencil/index.ts | 2 +- packages/presets/src/presets/svelte/config.ts | 76 +- packages/presets/src/presets/svelte/index.ts | 2 +- .../presets/src/presets/typescript/config.ts | 42 +- .../presets/src/presets/typescript/index.ts | 2 +- .../presets/src/presets/vitepress/config.ts | 74 +- .../presets/src/presets/vitepress/index.ts | 2 +- packages/presets/src/presets/vue/config.ts | 66 +- packages/presets/src/presets/vue/index.ts | 2 +- .../presets/src/presets/vuepress/config.ts | 66 +- .../presets/src/presets/vuepress/index.ts | 2 +- packages/presets/tsconfig.json | 1 + 80 files changed, 3502 insertions(+), 2571 deletions(-) delete mode 100644 packages/config/src/configProcessor/helpers/convertLegacyConfig.ts rename packages/config/src/configProcessor/processStrategy/implementations/{ => application}/cacheProcessConfigStrategy.ts (67%) create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts rename packages/config/src/configProcessor/processStrategy/implementations/{ => application}/rulesProcessConfigStrategy.ts (79%) delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts create mode 100644 packages/config/src/rules/constants.ts create mode 100644 packages/config/src/rules/index.ts create mode 100644 packages/config/src/rules/request/createMPA.ts create mode 100644 packages/config/src/rules/request/createSPA.ts create mode 100644 packages/config/tsup.config.ts diff --git a/package-lock.json b/package-lock.json index 84963ea7..ff4a6533 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "azion", - "version": "1.19.1", + "version": "1.20.0-stage.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "azion", - "version": "1.19.1", + "version": "1.20.0-stage.1", "license": "MIT", "workspaces": [ "packages/*" diff --git a/package.json b/package.json index 727db5cd..47710596 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,10 @@ "require": "./packages/config/dist/index.js", "import": "./packages/config/dist/index.mjs" }, + "./config/rules": { + "require": "./packages/config/dist/rules/index.js", + "import": "./packages/config/dist/rules/index.mjs" + }, "./sql": { "require": "./packages/sql/dist/index.js", "import": "./packages/sql/dist/index.mjs" @@ -165,6 +169,12 @@ "ai": [ "./packages/ai/dist/index.d.ts" ], + "config": [ + "./packages/config/dist/index.d.ts" + ], + "config/rules": [ + "./packages/config/dist/rules/index.d.ts" + ], "presets": [ "./packages/presets/dist/index.d.ts" ], diff --git a/packages/config/package.json b/packages/config/package.json index 40cd0b8b..c4fe9076 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -6,7 +6,7 @@ "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "scripts": { - "compile": "tsup --config ../../tsup.config.json", + "compile": "tsup --config ./tsup.config.ts", "lint": "eslint .", "lint:fix": "eslint --fix .", "prettier": "prettier --write .", @@ -14,6 +14,18 @@ "test:watch": "jest -c jest.config.js . --watch", "test:coverage": "jest --clearCache && jest -c jest.config.js . --coverage" }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.cjs", + "import": "./dist/index.js" + }, + "./rules": { + "types": "./dist/rules/index.d.ts", + "require": "./dist/rules/index.cjs", + "import": "./dist/rules/index.js" + } + }, "author": "aziontech", "license": "MIT", "files": [ diff --git a/packages/config/src/configProcessor/helpers/behaviors.ts b/packages/config/src/configProcessor/helpers/behaviors.ts index aa275406..8138ee5c 100644 --- a/packages/config/src/configProcessor/helpers/behaviors.ts +++ b/packages/config/src/configProcessor/helpers/behaviors.ts @@ -1,18 +1,16 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export const requestBehaviors = { - setOrigin: { + setEdgeConnector: { transform: (value: any, payloadCDN: any) => { - const origin = payloadCDN.origin?.find((o: any) => o.name === value.name && o.origin_type === value.type); - - if (!origin) { - throw new Error(`Rule setOrigin name '${value.name}' not found in the origin settings`); - } else if (origin.origin_type !== value.type) { - throw new Error(`Rule setOrigin originType '${value.type}' does not match the origin settings`); + const connectorName = typeof value === 'string' ? value : value.name; + const connector = payloadCDN.edgeConnectors?.find((o: any) => o.name === connectorName); + if (!connector) { + throw new Error(`Rule setEdgeConnector '${connectorName}' not found in the edge connectors list`); } return { - name: 'set_origin', - target: origin.name, + name: 'set_edge_connector', + target: connector.name, }; }, }, @@ -265,17 +263,14 @@ export const responseBehaviors = { }; export const revertRequestBehaviors = { - set_origin: { + set_edge_connector: { transform: (value: any, payloadCDN: any) => { - const origin = payloadCDN.origin?.find((o: any) => o.name === value); - if (!origin) { - throw new Error(`Rule setOrigin name '${value.name}' not found in the origin settings`); + const connector = payloadCDN.edgeConnectors?.find((o: any) => o.name === value); + if (!connector) { + throw new Error(`Rule setEdgeConnector name '${value.name}' not found in the edge connectors list`); } return { - setOrigin: { - name: value, - type: origin.type, - }, + setEdgeConnector: value, }; }, }, diff --git a/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts b/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts deleted file mode 100644 index 6f8e44ad..00000000 --- a/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Converts legacy configuration to the new format by moving old properties into the `behavior` object. - * This ensures backward compatibility for projects that do not use the `behavior` field. - * @param {object} config - The configuration object to be converted. - * @returns The converted configuration object with `behavior` properties. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function convertLegacyConfig(config: any) { - const newConfig = { ...config }; - - // Define the properties that should be moved to the behavior object for request and response - const requestBehaviorProperties = [ - 'httpToHttps', - 'bypassCache', - 'forwardCookies', - 'capture', - 'setOrigin', - 'rewrite', - 'setCookie', - 'setHeaders', - 'runFunction', - 'setCache', - 'redirectTo301', - 'redirectTo302', - 'deliver', - ]; - - const responseBehaviorProperties = [ - 'enableGZIP', - 'capture', - 'setCookie', - 'setHeaders', - 'filterHeader', - 'filterCookie', - 'runFunction', - 'redirectTo301', - 'redirectTo302', - 'deliver', - ]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const convertRules = (rules: any, behaviorProperties: any) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return rules.map((rule: any) => { - const newRule = { ...rule, behavior: { ...rule.behavior } }; - - // Preserve the order of properties - Object.keys(rule).forEach((prop) => { - if (behaviorProperties.includes(prop)) { - newRule.behavior[prop] = rule[prop]; - delete newRule[prop]; - } - }); - - return newRule; - }); - }; - - if (newConfig.rules && newConfig.rules.request) { - newConfig.rules.request = convertRules(newConfig.rules.request, requestBehaviorProperties); - } - - if (newConfig.rules && newConfig.rules.response) { - newConfig.rules.response = convertRules(newConfig.rules.response, responseBehaviorProperties); - } - return newConfig; -} - -export default convertLegacyConfig; diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 4880d56c..1cbe00d6 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -2,6 +2,9 @@ import { ALL_REQUEST_VARIABLES, ALL_RESPONSE_VARIABLES, DYNAMIC_VARIABLE_PATTERNS, + EDGE_CONNECTOR_CONNECTION_PREFERENCE, + EDGE_CONNECTOR_LOAD_BALANCE, + EDGE_CONNECTOR_TYPES, FIREWALL_RATE_LIMIT_BY, FIREWALL_RATE_LIMIT_TYPES, FIREWALL_VARIABLES, @@ -14,6 +17,11 @@ import { SPECIAL_VARIABLES, WAF_MODE, WAF_SENSITIVITY, + WORKLOAD_HTTP_VERSIONS, + WORKLOAD_MTLS_VERIFICATION, + WORKLOAD_NETWORK_MAP, + WORKLOAD_TLS_CIPHERS, + WORKLOAD_TLS_VERSIONS, } from '../../constants'; const criteriaBaseSchema = { @@ -156,24 +164,9 @@ const createRuleSchema = (isRequestPhase = false) => ({ behavior: { type: 'object', properties: { - setOrigin: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - type: { - type: 'string', - errorMessage: "The 'type' field must be a string.", - }, - }, - required: ['name', 'type'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'setOrigin' object.", - required: "The 'name or type' field is required in the 'setOrigin' object.", - }, + setEdgeConnector: { + type: 'string', + errorMessage: "The 'setEdgeConnector' field must be a string.", }, rewrite: { type: 'string', @@ -259,6 +252,10 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['wafMode', 'wafId'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the setWafRuleset object', + required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset", + }, }, setRateLimit: { type: 'object', @@ -284,6 +281,11 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the setRateLimit object', + required: + "All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit", + }, }, deny: { type: 'boolean', @@ -313,6 +315,10 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['statusCode', 'contentType', 'contentBody'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the setCustomResponse object', + required: "All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse", + }, }, tagEvent: { type: 'object', @@ -338,15 +344,15 @@ const createRuleSchema = (isRequestPhase = false) => ({ type: 'string', errorMessage: "The 'name' field must be a string.", }, - browser_cache_settings_maximum_ttl: { + browserCacheSettingsMaximumTtl: { type: 'number', nullable: true, - errorMessage: "The 'browser_cache_settings_maximum_ttl' field must be a number or null.", + errorMessage: "The 'browserCacheSettingsMaximumTtl' field must be a number or null.", }, - cdn_cache_settings_maximum_ttl: { + cdnCacheSettingsMaximumTtl: { type: 'number', nullable: true, - errorMessage: "The 'cdn_cache_settings_maximum_ttl' field must be a number or null.", + errorMessage: "The 'cdnCacheSettingsMaximumTtl' field must be a number or null.", }, }, required: ['name'], @@ -424,6 +430,84 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, }); +const schemaFunction = { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + args: { + type: 'object', + errorMessage: "The 'args' field must be an object", + }, + bindings: { + type: 'object', + properties: { + storage: { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + type: "The 'storage' field must be an object", + additionalProperties: 'No additional properties are allowed in the storage object', + required: "The 'bucket' field is required in the storage object", + }, + }, + }, + additionalProperties: false, + errorMessage: { + type: "The 'bindings' field must be an object", + additionalProperties: 'No additional properties are allowed in the bindings object', + }, + }, + }, + required: ['name', 'path'], + additionalProperties: false, +}; + +const schemaStorage = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 6, + maxLength: 63, + pattern: '^.{6,63}$', + errorMessage: "The 'name' field must be a string between 6 and 63 characters.", + }, + dir: { + type: 'string', + errorMessage: "The 'dir' field must be a string.", + }, + edgeAccess: { + type: 'string', + enum: ['read_only', 'read_write', 'restricted'], + errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", + }, + }, + required: ['name', 'dir'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in storage items.', + required: "The 'name' and 'dir' fields are required.", + }, +}; + const azionConfigSchema = { $id: 'azionConfig', definitions: { @@ -518,450 +602,404 @@ const azionConfigSchema = { additionalProperties: "No additional properties are allowed in the 'build' object", }, }, - functions: { + edgeApplications: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', - errorMessage: "The function's 'name' field must be a string", + minLength: 1, + maxLength: 250, + errorMessage: "The 'name' field must be a string with 1 to 250 characters", }, - path: { - type: 'string', - errorMessage: "The function's 'path' field must be a string", - }, - args: { + modules: { type: 'object', - additionalProperties: true, - errorMessage: "The function's 'args' field must be an object", - }, - }, - required: ['name', 'path'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in function items', - required: "Both 'name' and 'path' fields are required for each function", - }, - }, - }, - rules: { - type: 'object', - properties: { - request: { - type: 'array', - items: createRuleSchema(true), - }, - response: { - type: 'array', - items: createRuleSchema(false), - }, - }, - additionalProperties: false, - }, - origin: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'integer', - errorMessage: "The 'id' field must be a number.", - }, - key: { - type: 'string', - errorMessage: "The 'key' field must be a string.", - }, - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - type: { - type: 'string', - enum: ['single_origin', 'object_storage', 'load_balancer', 'live_ingest'], - errorMessage: - "The 'type' field must be a string and one of 'single_origin', 'object_storage', 'load_balancer' or 'live_ingest'.", + properties: { + edgeCacheEnabled: { + type: 'boolean', + default: true, + errorMessage: "The 'edgeCacheEnabled' field must be a boolean", + }, + edgeFunctionsEnabled: { + type: 'boolean', + default: false, + errorMessage: "The 'edgeFunctionsEnabled' field must be a boolean", + }, + applicationAcceleratorEnabled: { + type: 'boolean', + default: false, + errorMessage: "The 'applicationAcceleratorEnabled' field must be a boolean", + }, + imageProcessorEnabled: { + type: 'boolean', + default: false, + errorMessage: "The 'imageProcessorEnabled' field must be a boolean", + }, + tieredCacheEnabled: { + type: 'boolean', + default: false, + errorMessage: "The 'tieredCacheEnabled' field must be a boolean", + }, + }, + required: [], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in modules object', + }, }, - bucket: { - type: ['string', 'null'], - errorMessage: "The 'bucket' field must be a string or null.", + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean", }, - prefix: { - type: ['string', 'null'], - errorMessage: "The 'prefix' field must be a string or null.", + debug: { + type: 'boolean', + default: false, + errorMessage: "The 'debug' field must be a boolean", }, - addresses: { - anyOf: [ - { - type: 'array', - items: { + cache: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string', + errorMessage: "The 'name' field must be a string.", }, - errorMessage: { - type: "The 'addresses' field must be an array of strings.", + stale: { + type: 'boolean', + errorMessage: "The 'stale' field must be a boolean.", }, - }, - { - type: 'array', - items: { + queryStringSort: { + type: 'boolean', + errorMessage: "The 'queryStringSort' field must be a boolean.", + }, + methods: { type: 'object', properties: { - address: { + post: { + type: 'boolean', + errorMessage: "The 'post' field must be a boolean.", + }, + options: { + type: 'boolean', + errorMessage: "The 'options' field must be a boolean.", + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'methods' object.", + }, + }, + browser: { + type: 'object', + properties: { + maxAgeSeconds: { + oneOf: [ + { + type: 'number', + errorMessage: + "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", + }, + { + type: 'string', + pattern: '^[0-9+*/.() -]+$', + errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", + }, + ], + }, + }, + required: ['maxAgeSeconds'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'browser' object.", + required: "The 'maxAgeSeconds' field is required in the 'browser' object.", + }, + }, + edge: { + type: 'object', + properties: { + maxAgeSeconds: { + oneOf: [ + { + type: 'number', + errorMessage: + "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", + }, + { + type: 'string', + pattern: '^[0-9+*/.() -]+$', + errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", + }, + ], + }, + }, + required: ['maxAgeSeconds'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'edge' object.", + required: "The 'maxAgeSeconds' field is required in the 'edge' object.", + }, + }, + cacheByCookie: { + type: 'object', + properties: { + option: { + type: 'string', + enum: ['ignore', 'varies', 'whitelist', 'blacklist'], + errorMessage: + "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'..", + }, + list: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each item in 'list' must be a string.", + }, + errorMessage: { + type: "The 'list' field must be an array of strings.", + }, + }, + }, + required: ['option'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'cacheByCookie' object.", + required: "The 'option' field is required in the 'cacheByCookie' object.", + }, + if: { + properties: { + option: { enum: ['whitelist', 'blacklist'] }, + }, + }, + then: { + required: ['list'], + errorMessage: { + required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", + }, + }, + }, + cacheByQueryString: { + type: 'object', + properties: { + option: { type: 'string', - errorMessage: "The 'address' field must be a string.", + enum: ['ignore', 'varies', 'whitelist', 'blacklist'], + errorMessage: + "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'.", }, - weight: { - type: 'integer', + list: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each item in 'list' must be a string.", + }, + errorMessage: { + type: "The 'list' field must be an array of strings.", + }, }, }, - required: ['address'], + required: ['option'], additionalProperties: false, errorMessage: { - type: "The 'addresses' field must be an array of objects.", - additionalProperties: 'No additional properties are allowed in address items.', - required: "The 'address' field is required in each address item.", + additionalProperties: + "No additional properties are allowed in the 'cacheByQueryString' object.", + required: "The 'option' field is required in the 'cacheByQueryString' object.", + }, + if: { + properties: { + option: { enum: ['whitelist', 'blacklist'] }, + }, + }, + then: { + required: ['list'], + errorMessage: { + required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", + }, }, }, }, - ], - }, - hostHeader: { - type: 'string', - errorMessage: "The 'hostHeader' field must be a string.", - }, - protocolPolicy: { - type: 'string', - enum: ['preserve', 'http', 'https'], - errorMessage: - "The 'protocolPolicy' field must be either 'http', 'https' or 'preserve'. Default is 'preserve'.", - }, - redirection: { - type: 'boolean', - errorMessage: "The 'redirection' field must be a boolean.", - }, - method: { - type: 'string', - enum: ['ip_hash', 'least_connections', 'round_robin'], - errorMessage: - "The 'method' field must be either 'ip_hash', 'least_connections' or 'round_robin'. Default is 'ip_hash'.", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string.", - }, - connectionTimeout: { - type: 'integer', - errorMessage: "The 'connectionTimeout' field must be a number. Default is 60.", - }, - timeoutBetweenBytes: { - type: 'integer', - errorMessage: "The 'timeoutBetweenBytes' field must be a number. Default is 120.", + required: ['name'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in cache item objects.', + required: "The 'name' field is required in each cache item.", + }, + }, }, - hmac: { + rules: { type: 'object', properties: { - region: { - type: 'string', - errorMessage: "The 'region' field must be a string.", - }, - accessKey: { - type: 'string', - errorMessage: "The 'accessKey' field must be a string.", + request: { + type: 'array', + items: createRuleSchema(true), }, - secretKey: { - type: 'string', - errorMessage: "The 'secretKey' field must be a string.", + response: { + type: 'array', + items: createRuleSchema(false), }, }, - required: ['region', 'accessKey', 'secretKey'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the hmac object.', - required: "The 'region, accessKey and secretKey' fields are required in the hmac object.", - }, }, }, - required: ['name', 'type'], + required: ['name'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in origin item objects.', - required: "The 'name and type' field is required in each origin item.", - }, - }, - errorMessage: { - additionalProperties: "The 'origin' field must be an array of objects.", }, + errorMessage: "The 'edgeApplications' field must be an array of application objects", }, - cache: { + workloads: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', - errorMessage: "The 'name' field must be a string.", + minLength: 1, + maxLength: 100, + errorMessage: "The 'name' field must be a string between 1 and 100 characters", + }, + alternateDomains: { + type: 'array', + items: { type: 'string' }, + maxItems: 50, + errorMessage: "The 'alternateDomains' field must be an array of strings with max 50 items", + }, + edgeApplication: { + type: 'string', + errorMessage: "The 'edgeApplication' field must be a string", }, - stale: { + active: { type: 'boolean', - errorMessage: "The 'stale' field must be a boolean.", + default: true, + errorMessage: "The 'active' field must be a boolean", + }, + networkMap: { + type: 'string', + enum: WORKLOAD_NETWORK_MAP, + default: '1', + errorMessage: "The 'networkMap' must be either '1' or '2'", + }, + edgeFirewall: { + type: ['string', 'null'], + errorMessage: "The 'edgeFirewall' must be an integer or null", }, - queryStringSort: { + workloadHostnameAllowAccess: { type: 'boolean', - errorMessage: "The 'queryStringSort' field must be a boolean.", + default: true, + errorMessage: "The 'workloadHostnameAllowAccess' field must be a boolean", }, - methods: { + domains: { + type: 'array', + items: { + type: 'string', + minLength: 1, + maxLength: 250, + errorMessage: 'Each domain must be a string between 1 and 250 characters', + }, + minItems: 1, + errorMessage: "The 'domains' field must be an array of domain strings", + }, + tls: { type: 'object', properties: { - post: { - type: 'boolean', - errorMessage: "The 'post' field must be a boolean.", + certificate: { + type: ['integer', 'null'], + minimum: 1, + errorMessage: "The 'certificate' must be an integer >= 1 or null", }, - options: { - type: 'boolean', - errorMessage: "The 'options' field must be a boolean.", + ciphers: { + type: ['string', 'null'], + enum: [...WORKLOAD_TLS_CIPHERS, 'null'], + errorMessage: "The 'ciphers' must be a valid TLS cipher suite or null", + }, + minimumVersion: { + type: ['string', 'null'], + enum: [...WORKLOAD_TLS_VERSIONS, 'null'], + default: 'tls_1_2', + errorMessage: "The 'minimumVersion' must be a valid TLS version or null", }, }, + default: { certificate: null, ciphers: null, minimumVersion: 'tls_1_2' }, additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'methods' object.", - }, }, - browser: { + protocols: { type: 'object', properties: { - maxAgeSeconds: { - oneOf: [ - { - type: 'number', - errorMessage: "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", + http: { + type: 'object', + properties: { + versions: { + type: 'array', + items: { + type: 'string', + enum: WORKLOAD_HTTP_VERSIONS, + }, + default: ['http1', 'http2'], }, - { - type: 'string', - pattern: '^[0-9+*/.() -]+$', - errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", + httpPorts: { + type: 'array', + items: { type: 'integer' }, + default: [80], }, - ], - }, - }, - required: ['maxAgeSeconds'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'browser' object.", - required: "The 'maxAgeSeconds' field is required in the 'browser' object.", - }, - }, - edge: { - type: 'object', - properties: { - maxAgeSeconds: { - oneOf: [ - { - type: 'number', - errorMessage: "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", + httpsPorts: { + type: 'array', + items: { type: 'integer' }, + default: [443], }, - { - type: 'string', - pattern: '^[0-9+*/.() -]+$', - errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", + quicPorts: { + type: ['array', 'null'], + items: { type: 'integer' }, }, - ], + }, + required: ['versions', 'httpPorts', 'httpsPorts'], + additionalProperties: false, }, }, - required: ['maxAgeSeconds'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'edge' object.", - required: "The 'maxAgeSeconds' field is required in the 'edge' object.", + default: { + http: { + versions: ['http1', 'http2'], + httpPorts: [80], + httpsPorts: [443], + quicPorts: null, + }, }, + additionalProperties: false, }, - cacheByCookie: { + mtls: { type: 'object', properties: { - option: { + verification: { type: 'string', - enum: ['ignore', 'varies', 'whitelist', 'blacklist'], - errorMessage: "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'..", + enum: WORKLOAD_MTLS_VERIFICATION, + default: 'enforce', }, - list: { - type: 'array', - items: { - type: 'string', - errorMessage: "Each item in 'list' must be a string.", - }, - errorMessage: { - type: "The 'list' field must be an array of strings.", - }, - }, - }, - required: ['option'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'cacheByCookie' object.", - required: "The 'option' field is required in the 'cacheByCookie' object.", - }, - if: { - properties: { - option: { enum: ['whitelist', 'blacklist'] }, - }, - }, - then: { - required: ['list'], - errorMessage: { - required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", + certificate: { + type: ['integer', 'null'], + minimum: 1, }, - }, - }, - - cacheByQueryString: { - type: 'object', - properties: { - option: { - type: 'string', - enum: ['ignore', 'varies', 'whitelist', 'blacklist'], - errorMessage: "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'.", - }, - list: { - type: 'array', - items: { - type: 'string', - errorMessage: "Each item in 'list' must be a string.", - }, - errorMessage: { - type: "The 'list' field must be an array of strings.", - }, + crl: { + type: ['array', 'null'], + items: { type: 'integer' }, + maxItems: 100, }, }, - required: ['option'], + required: ['verification'], additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'cacheByQueryString' object.", - required: "The 'option' field is required in the 'cacheByQueryString' object.", - }, - if: { - properties: { - option: { enum: ['whitelist', 'blacklist'] }, - }, - }, - then: { - required: ['list'], - errorMessage: { - required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", - }, - }, }, }, - required: ['name'], + required: ['name', 'edgeApplication', 'domains'], additionalProperties: false, errorMessage: { - additionalProperties: 'No additional properties are allowed in cache item objects.', - required: "The 'name' field is required in each cache item.", - }, - }, - errorMessage: { - additionalProperties: "The 'cache' field must be an array of objects.", - }, - }, - networkList: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'number', - errorMessage: "The 'id' field must be a number.", - }, - listType: { - type: 'string', - enum: NETWORK_LIST_TYPES, - errorMessage: - "The 'listType' field must be a string. Accepted values are 'ip_cidr', 'asn' or 'countries'.", - }, - listContent: { - type: 'array', - items: { - type: ['string', 'number'], - errorMessage: "The 'listContent' field must be an array of strings or numbers.", - }, - }, - }, - required: ['id', 'listType', 'listContent'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in network list items.', - required: "The 'id, listType and listContent' fields are required in each network list item.", - }, - }, - }, - domain: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - cnameAccessOnly: { - type: 'boolean', - errorMessage: "The 'cnameAccessOnly' field must be a boolean.", - }, - cnames: { - type: 'array', - items: { - type: 'string', - errorMessage: "Each item in 'cnames' must be a string.", - }, - errorMessage: { - type: "The 'cnames' field must be an array of strings.", - }, - }, - edgeApplicationId: { - type: 'number', - errorMessage: "The 'edgeApplicationId' field must be a number.", - }, - edgeFirewallId: { - type: 'number', - errorMessage: "The 'edgeFirewallId' field must be a number.", - }, - digitalCertificateId: { - type: ['string', 'number', 'null'], - errorMessage: - "The 'digitalCertificateId' field must be a string, number or null. If string, it must be 'lets_encrypt'.", - }, - active: { - type: 'boolean', - errorMessage: "The 'active' field must be a boolean.", - }, - mtls: { - type: 'object', - properties: { - verification: { - type: 'string', - enum: ['enforce', 'permissive'], - errorMessage: "The 'verification' field must be a string.", - }, - trustedCaCertificateId: { - type: 'number', - errorMessage: "The 'trustedCaCertificateId' field must be a number.", - }, - crlList: { - type: 'array', - items: { - type: 'number', - errorMessage: "Each item in 'crlList' must be a number.", - }, - errorMessage: { - type: "The 'crlList' field must be an array of numbers.", - }, - }, - }, - required: ['verification', 'trustedCaCertificateId'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the mtls object.', - required: "The 'verification and trustedCaCertificateId' fields are required in the mtls object.", + additionalProperties: 'No additional properties are allowed in workload items', + required: { + name: "The 'name' field is required in workloads", + edgeApplication: "The 'edgeApplication' field is required in workloads", + domains: "The 'domains' field is required in workloads", }, }, }, - additionalProperties: false, + errorMessage: "The 'workloads' field must be an array of workloads items.", }, purge: { type: 'array', @@ -1003,155 +1041,239 @@ const azionConfigSchema = { }, }, }, - firewall: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The firewall configuration must have a 'name' field of type string", - }, - domains: { - type: 'array', - items: { + edgeFirewall: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string', - errorMessage: "Each domain in the firewall's domains list must be a string", + errorMessage: "The edgeFirewall configuration must have a 'name' field of type string", }, - }, - active: { - type: 'boolean', - errorMessage: "The firewall's 'active' field must be a boolean", - }, - debugRules: { - type: 'boolean', - errorMessage: "The firewall's 'debugRules' field must be a boolean", - }, - edgeFunctions: { - type: 'boolean', - errorMessage: "The firewall's 'edgeFunctions' field must be a boolean", - }, - networkProtection: { - type: 'boolean', - errorMessage: "The firewall's 'networkProtection' field must be a boolean", - }, - waf: { - type: 'boolean', - errorMessage: "The firewall's 'waf' field must be a boolean", - }, - variable: { - type: 'string', - enum: FIREWALL_VARIABLES, - errorMessage: `The 'variable' field must be one of: ${FIREWALL_VARIABLES.join(', ')}`, - }, - rules: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "Each firewall rule must have a 'name' field of type string", - }, - description: { - type: 'string', - errorMessage: "The rule's 'description' field must be a string", - }, - active: { - type: 'boolean', - errorMessage: "The rule's 'active' field must be a boolean", - }, - match: { - type: 'string', - errorMessage: "The rule's 'match' field must be a string containing a valid regex pattern", - }, - behavior: { - type: 'object', - properties: { - runFunction: { - type: 'string', - errorMessage: "The 'runFunction' behavior must be a string", - }, - setWafRuleset: { - type: 'object', - properties: { - wafMode: { - type: 'string', - enum: FIREWALL_WAF_MODES, + domains: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each domain in the edge firewall's domains list must be a string", + }, + }, + active: { + type: 'boolean', + errorMessage: "The firewall's 'active' field must be a boolean", + }, + debugRules: { + type: 'boolean', + errorMessage: "The firewall's 'debugRules' field must be a boolean", + }, + edgeFunctions: { + type: 'boolean', + errorMessage: "The firewall's 'edgeFunctions' field must be a boolean", + }, + networkProtection: { + type: 'boolean', + errorMessage: "The firewall's 'networkProtection' field must be a boolean", + }, + waf: { + type: 'boolean', + errorMessage: "The firewall's 'waf' field must be a boolean", + }, + variable: { + type: 'string', + enum: FIREWALL_VARIABLES, + errorMessage: `The 'variable' field must be one of: ${FIREWALL_VARIABLES.join(', ')}`, + }, + rules: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "Each firewall rule must have a 'name' field of type string", + }, + description: { + type: 'string', + errorMessage: "The rule's 'description' field must be a string", + }, + active: { + type: 'boolean', + errorMessage: "The rule's 'active' field must be a boolean", + }, + match: { + type: 'string', + errorMessage: "The rule's 'match' field must be a string containing a valid regex pattern", + }, + behavior: { + type: 'object', + properties: { + runFunction: { + type: 'string', + errorMessage: "The 'runFunction' behavior must be a string", + }, + setWafRuleset: { + type: 'object', + properties: { + wafMode: { + type: 'string', + enum: FIREWALL_WAF_MODES, + errorMessage: `The wafMode must be one of: ${FIREWALL_WAF_MODES.join(', ')}`, + }, + wafId: { + type: 'string', + errorMessage: 'The wafId must be a string', + }, + }, + required: ['wafMode', 'wafId'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the setWafRuleset object', + required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset", }, - wafId: { type: 'string' }, }, - required: ['wafMode', 'wafId'], - }, - setRateLimit: { - type: 'object', - properties: { - type: { - type: 'string', - enum: FIREWALL_RATE_LIMIT_TYPES, + setRateLimit: { + type: 'object', + properties: { + type: { + type: 'string', + enum: FIREWALL_RATE_LIMIT_TYPES, + errorMessage: `The rate limit type must be one of: ${FIREWALL_RATE_LIMIT_TYPES.join(', ')}`, + }, + limitBy: { + type: 'string', + enum: FIREWALL_RATE_LIMIT_BY, + errorMessage: `The rate limit must be applied by one of: ${FIREWALL_RATE_LIMIT_BY.join(', ')}`, + }, + averageRateLimit: { + type: 'string', + errorMessage: 'The averageRateLimit must be a string', + }, + maximumBurstSize: { + type: 'string', + errorMessage: 'The maximumBurstSize must be a string', + }, }, - limitBy: { - type: 'string', - enum: FIREWALL_RATE_LIMIT_BY, + required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the setRateLimit object', + required: + "All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit", }, - averageRateLimit: { type: 'string' }, - maximumBurstSize: { type: 'string' }, }, - required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], - }, - deny: { type: 'boolean' }, - drop: { type: 'boolean' }, - setCustomResponse: { - type: 'object', - properties: { - statusCode: { type: ['integer', 'string'], minimum: 200, maximum: 499 }, - contentType: { type: 'string' }, - contentBody: { type: 'string' }, + deny: { + type: 'boolean', + errorMessage: 'The deny behavior must be a boolean', + }, + drop: { + type: 'boolean', + errorMessage: 'The drop behavior must be a boolean', + }, + setCustomResponse: { + type: 'object', + properties: { + statusCode: { + type: ['integer', 'string'], + minimum: 200, + maximum: 499, + errorMessage: 'The statusCode must be a number or string between 200 and 499', + }, + contentType: { + type: 'string', + errorMessage: 'The contentType must be a string', + }, + contentBody: { + type: 'string', + errorMessage: 'The contentBody must be a string', + }, + }, + required: ['statusCode', 'contentType', 'contentBody'], + additionalProperties: false, + errorMessage: { + additionalProperties: + 'No additional properties are allowed in the setCustomResponse object', + required: + "All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse", + }, }, - required: ['statusCode', 'contentType', 'contentBody'], }, + not: { + anyOf: [ + { required: ['deny', 'drop'] }, + { required: ['deny', 'setCustomResponse'] }, + { required: ['deny', 'setRateLimit'] }, + { required: ['drop', 'setCustomResponse'] }, + { required: ['drop', 'setRateLimit'] }, + { required: ['setCustomResponse', 'setRateLimit'] }, + ], + }, + errorMessage: { + not: 'Cannot use multiple final behaviors (deny, drop, setRateLimit, setCustomResponse) together. You can combine non-final behaviors (runFunction, setWafRuleset) with only one final behavior.', + }, + additionalProperties: false, }, - not: { - anyOf: [ - { required: ['deny', 'drop'] }, - { required: ['deny', 'setCustomResponse'] }, - { required: ['deny', 'setRateLimit'] }, - { required: ['drop', 'setCustomResponse'] }, - { required: ['drop', 'setRateLimit'] }, - { required: ['setCustomResponse', 'setRateLimit'] }, - ], - }, - errorMessage: { - not: 'Cannot use multiple final behaviors (deny, drop, setRateLimit, setCustomResponse) together. You can combine non-final behaviors (runFunction, setWafRuleset) with only one final behavior.', - }, - additionalProperties: false, }, - }, - required: ['name', 'behavior'], - oneOf: [ - { - anyOf: [{ required: ['match'] }, { required: ['variable'] }], - not: { required: ['criteria'] }, - errorMessage: "Cannot use 'match' or 'variable' together with 'criteria'.", - }, - { - required: ['criteria'], - not: { + required: ['name', 'behavior'], + oneOf: [ + { anyOf: [{ required: ['match'] }, { required: ['variable'] }], + not: { required: ['criteria'] }, + errorMessage: "Cannot use 'match' or 'variable' together with 'criteria'.", + }, + { + required: ['criteria'], + not: { + anyOf: [{ required: ['match'] }, { required: ['variable'] }], + }, + errorMessage: "Cannot use 'criteria' together with 'match' or 'variable'.", }, - errorMessage: "Cannot use 'criteria' together with 'match' or 'variable'.", + ], + errorMessage: { + oneOf: "You must use either 'match/variable' OR 'criteria', but not both at the same time", }, - ], - errorMessage: { - oneOf: "You must use either 'match/variable' OR 'criteria', but not both at the same time", }, }, }, + required: ['name'], + additionalProperties: false, + errorMessage: { + type: 'Each edgeFirewall item must be an object', + additionalProperties: 'No additional properties are allowed in the edgeFirewall object', + required: "The 'name' field is required in each edge firewall object", + }, }, - required: ['name'], - additionalProperties: false, errorMessage: { - type: "The 'firewall' field must be an object", - additionalProperties: 'No additional properties are allowed in the firewall object', - required: "The 'name' field is required in the firewall object", + type: "The 'edgeFirewall' field must be an array of edge firewall objects", + }, + }, + networkList: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'number', + errorMessage: "The 'id' field must be a number.", + }, + listType: { + type: 'string', + enum: NETWORK_LIST_TYPES, + errorMessage: + "The 'listType' field must be a string. Accepted values are 'ip_cidr', 'asn' or 'countries'.", + }, + listContent: { + type: 'array', + items: { + type: ['string', 'number'], + errorMessage: "The 'listContent' field must be an array of strings or numbers.", + }, + }, + }, + required: ['id', 'listType', 'listContent'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in network list items.', + required: "The 'id, listType and listContent' fields are required in each network list item.", + }, }, }, waf: { @@ -1295,11 +1417,281 @@ const azionConfigSchema = { type: "The 'waf' field must be an array", }, }, + edgeConnectors: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 1, + maxLength: 255, + errorMessage: "The 'name' field must be a string between 1 and 255 characters", + }, + modules: { + type: 'object', + properties: { + loadBalancerEnabled: { + type: 'boolean', + errorMessage: "'loadBalancerEnabled' must be a boolean", + }, + originShieldEnabled: { + type: 'boolean', + errorMessage: "'originShieldEnabled' must be a boolean", + }, + }, + required: ['originShieldEnabled', 'loadBalancerEnabled'], + additionalProperties: false, + errorMessage: { + required: "'loadBalancerEnabled' and 'originShieldEnabled' are required in modules", + additionalProperties: 'No additional properties are allowed in modules', + }, + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean", + }, + type: { + type: 'string', + enum: EDGE_CONNECTOR_TYPES, + errorMessage: "The 'type' field must be one of: http, s3, edge_storage, live_ingest", + }, + typeProperties: { + oneOf: [ + { + type: 'object', + properties: { + versions: { + type: 'array', + items: { type: 'string' }, + errorMessage: "The 'versions' field must be an array of strings", + }, + host: { + type: 'string', + errorMessage: "The 'host' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + followingRedirect: { + type: 'boolean', + errorMessage: "The 'followingRedirect' field must be a boolean", + }, + realIpHeader: { + type: 'string', + errorMessage: "The 'realIpHeader' field must be a string", + }, + realPortHeader: { + type: 'string', + errorMessage: "The 'realPortHeader' field must be a string", + }, + }, + required: ['versions', 'host', 'path'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in HTTP type properties', + required: "The 'versions', 'host', and 'path' fields are required for HTTP type", + }, + }, + { + type: 'object', + properties: { + endpoint: { + type: 'string', + errorMessage: "The 'endpoint' field must be a string", + }, + }, + required: ['endpoint'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in Live Ingest type properties', + required: "The 'endpoint' field is required for Live Ingest type", + }, + }, + { + type: 'object', + properties: { + host: { + type: 'string', + errorMessage: "The 'host' field must be a string", + }, + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + region: { + type: 'string', + errorMessage: "The 'region' field must be a string", + }, + accessKey: { + type: 'string', + errorMessage: "The 'accessKey' field must be a string", + }, + secretKey: { + type: 'string', + errorMessage: "The 'secretKey' field must be a string", + }, + }, + required: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in S3 type properties', + required: 'All fields are required for S3 type', + }, + }, + { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in Storage type properties', + required: "The 'bucket' field is required for Storage type", + }, + }, + ], + }, + addresses: { + type: 'array', + items: { + type: 'object', + properties: { + address: { + type: 'string', + minLength: 1, + maxLength: 255, + errorMessage: "The 'address' field must be a string between 1 and 255 characters", + }, + plainPort: { + type: 'integer', + minimum: 1, + maximum: 65535, + default: 80, + errorMessage: "The 'plainPort' field must be between 1 and 65535", + }, + tlsPort: { + type: 'integer', + minimum: 1, + maximum: 65535, + default: 443, + errorMessage: "The 'tlsPort' field must be between 1 and 65535", + }, + serverRole: { + type: 'string', + enum: ['primary', 'backup'], + default: 'primary', + errorMessage: "The 'serverRole' field must be either 'primary' or 'backup'", + }, + weight: { + type: 'integer', + minimum: 0, + maximum: 100, + default: 1, + errorMessage: "The 'weight' field must be between 0 and 100", + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'maxConns' field must be between 0 and 1000", + }, + maxConns: { + type: 'integer', + minimum: 0, + maximum: 1000, + default: 0, + errorMessage: "The 'maxFails' field must be between 1 and 10", + }, + maxFails: { + type: 'integer', + minimum: 1, + maximum: 10, + default: 1, + errorMessage: "The 'failTimeout' field must be between 1 and 60", + }, + failTimeout: { + type: 'integer', + minimum: 1, + maximum: 60, + default: 10, + errorMessage: "O campo 'failTimeout' deve estar entre 1 e 60", + }, + }, + required: ['address'], + additionalProperties: false, + }, + }, + tls: { + type: 'object', + properties: { + policy: { type: 'string' }, + }, + default: { policy: 'preserve' }, + }, + loadBalanceMethod: { + type: 'string', + enum: EDGE_CONNECTOR_LOAD_BALANCE, + default: 'off', + }, + connectionPreference: { + type: 'array', + items: { + type: 'string', + enum: EDGE_CONNECTOR_CONNECTION_PREFERENCE, + }, + maxItems: 2, + default: ['IPv6', 'IPv4'], + }, + connectionTimeout: { + type: 'integer', + minimum: 1, + maximum: 300, + default: 60, + }, + readWriteTimeout: { + type: 'integer', + minimum: 1, + maximum: 300, + default: 120, + }, + maxRetries: { + type: 'integer', + minimum: 0, + maximum: 10, + }, + }, + required: ['name', 'modules', 'type'], + additionalProperties: false, + }, + }, + edgeFunctions: { + type: 'array', + items: schemaFunction, + }, + edgeStorage: { + type: 'array', + items: schemaStorage, + errorMessage: "The 'edgeStorage' field must be an array of edge storage items.", + }, }, additionalProperties: false, errorMessage: { additionalProperties: - 'Config can only contain the following properties: build, functions, rules, origin, cache, networkList, domain, purge, firewall', + 'Config can only contain the following properties: build, edgeFunctions, edgeApplications, workloads, purge, edgefirewall, networkList, waf, edgeConnectors', type: 'Configuration must be an object', }, }, diff --git a/packages/config/src/configProcessor/helpers/schemaManifest.ts b/packages/config/src/configProcessor/helpers/schemaManifest.ts index 41d9e665..abe84d3b 100644 --- a/packages/config/src/configProcessor/helpers/schemaManifest.ts +++ b/packages/config/src/configProcessor/helpers/schemaManifest.ts @@ -10,16 +10,16 @@ import { CACHE_BY_QUERY_STRING, CACHE_CDN_SETTINGS, CACHE_L2_REGION, + EDGE_CONNECTOR_CONNECTION_PREFERENCE, + EDGE_CONNECTOR_LOAD_BALANCE, + EDGE_CONNECTOR_TYPES, FIREWALL_BEHAVIOR_NAMES, FIREWALL_RATE_LIMIT_BY, FIREWALL_RATE_LIMIT_TYPES, FIREWALL_RULE_CONDITIONALS, FIREWALL_RULE_OPERATORS, FIREWALL_VARIABLES, - LOAD_BALANCER_METHODS, NETWORK_LIST_TYPES, - ORIGIN_PROTOCOL_POLICIES, - ORIGIN_TYPES, RULE_BEHAVIOR_NAMES, RULE_CONDITIONALS, RULE_OPERATORS_WITH_VALUE, @@ -28,6 +28,11 @@ import { RULE_VARIABLES, WAF_MODE, WAF_SENSITIVITY, + WORKLOAD_HTTP_VERSIONS, + WORKLOAD_MTLS_VERIFICATION, + WORKLOAD_NETWORK_MAP, + WORKLOAD_TLS_CIPHERS, + WORKLOAD_TLS_VERSIONS, } from '../../constants'; const schemaNetworkListManifest = { @@ -189,99 +194,6 @@ const schemaWafManifest = { }, }; -const schemaDomainsManifest = { - type: ['object', 'null'], - properties: { - id: { - type: 'number', - errorMessage: "The 'id' field must be a number.", - }, - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - edge_application_id: { - type: 'number', - errorMessage: "The 'edge_application_id' field must be a number.", - }, - cnames: { - type: 'array', - items: { - type: 'string', - }, - errorMessage: "The 'cnames' field must be an array of strings.", - }, - cname_access_only: { - type: 'boolean', - errorMessage: "The 'cname_access_only' field must be a boolean.", - }, - digital_certificate_id: { - oneOf: [{ type: 'number' }, { type: 'string', enum: ['lets_encrypt'] }, { type: 'null' }], - errorMessage: "The 'digital_certificate_id' field must be a number, 'lets_encrypt' or null.", - }, - is_active: { - type: 'boolean', - default: true, - errorMessage: "The 'is_active' field must be a boolean.", - }, - domain_name: { - type: 'string', - errorMessage: "The 'domain_name' field must be a string.", - }, - is_mtls_enabled: { - type: 'boolean', - errorMessage: "The 'is_mtls_enabled' field must be a boolean.", - }, - mtls_verification: { - type: 'string', - enum: ['enforce', 'permissive'], - errorMessage: "The 'mtls_verification' field must be either 'enforce' or 'permissive'.", - }, - mtls_trusted_ca_certificate_id: { - type: 'number', - errorMessage: "The 'mtls_trusted_ca_certificate_id' field must be a number.", - }, - edge_firewall_id: { - type: 'number', - errorMessage: "The 'edge_firewall_id' field must be a number.", - }, - crl_list: { - type: 'array', - items: { - type: 'number', - }, - errorMessage: "The 'crl_list' field must be an array of numbers.", - }, - }, - required: ['name'], - dependencies: { - is_mtls_enabled: { - oneOf: [ - { - properties: { - is_mtls_enabled: { enum: [false] }, - }, - }, - { - properties: { - is_mtls_enabled: { enum: [true] }, - }, - required: ['mtls_verification', 'mtls_trusted_ca_certificate_id'], - }, - ], - }, - }, - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in domain items.', - required: - "The 'name', 'edge_application_id', 'cnames', 'cname_access_only', and 'digital_certificate_id' fields are required.", - dependencies: { - is_mtls_enabled: "When mTLS is enabled, 'mtls_verification' and 'mtls_trusted_ca_certificate_id' are required.", - }, - }, -}; - const schemaFirewallRuleCriteria = { type: 'object', properties: { @@ -313,6 +225,34 @@ const schemaFirewallRuleCriteria = { }, }; +const schemaStorageManifest = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 6, + maxLength: 63, + pattern: '^.{6,63}$', + errorMessage: "The 'name' field must be a string between 6 and 63 characters.", + }, + dir: { + type: 'string', + errorMessage: "The 'dir' field must be a string.", + }, + edge_access: { + type: 'string', + enum: ['read_only', 'read_write', 'restricted'], + errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", + }, + }, + required: ['name', 'dir'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in edge storage items.', + required: "The 'name' and 'dir' fields are required.", + }, +}; + const schemaFirewallRuleBehaviorArguments = { set_rate_limit: { type: 'object', @@ -344,32 +284,58 @@ const schemaFirewallRuleBehaviorArguments = { set_waf_ruleset: { type: 'object', properties: { - waf_id: { type: 'integer' }, + waf_id: { + type: 'integer', + errorMessage: 'The waf_id must be an integer', + }, mode: { type: 'string', enum: WAF_MODE, + errorMessage: `The mode must be one of: ${WAF_MODE.join(', ')}`, }, }, required: ['waf_id', 'mode'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the set_waf_ruleset object', + required: "Both 'waf_id' and 'mode' fields are required in set_waf_ruleset", + }, }, set_custom_response: { type: 'object', properties: { status_code: { oneOf: [ - { type: 'integer', minimum: 200, maximum: 499 }, - { type: 'string', pattern: '^[2-4][0-9]{2}$' }, + { + type: 'integer', + minimum: 200, + maximum: 499, + errorMessage: 'The status_code must be an integer between 200 and 499', + }, + { + type: 'string', + pattern: '^[2-4][0-9]{2}$', + errorMessage: 'The status_code as string must be a valid HTTP status code (200-499)', + }, ], + errorMessage: 'The status_code must be either an integer or string between 200 and 499', }, content_body: { type: 'string', maxLength: 500, + errorMessage: 'The content_body must be a string with maximum 500 characters', + }, + content_type: { + type: 'string', + errorMessage: 'The content_type must be a string', }, - content_type: { type: 'string' }, }, required: ['status_code', 'content_body', 'content_type'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the set_custom_response object', + required: "All fields ('status_code', 'content_body', 'content_type') are required in set_custom_response", + }, }, }; @@ -431,14 +397,10 @@ const schemaFirewallRule = { pattern: '^[\\u0000-\\uFFFF]*$', errorMessage: "The 'description' must not exceed 1000 characters and must not contain 4-byte unicode characters.", }, - is_active: { + active: { type: 'boolean', default: true, - errorMessage: "The 'is_active' field must be a boolean.", - }, - order: { - type: 'integer', - errorMessage: "The 'order' field must be an integer.", + errorMessage: "The 'active' field must be a boolean.", }, }, required: ['name', 'criteria', 'behaviors'], @@ -449,6 +411,57 @@ const schemaFirewallRule = { additionalProperties: 'No additional properties are allowed in firewall rules.', }, }; +const schemaFunctionManifest = { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string", + }, + target: { + type: 'string', + errorMessage: "The 'target' field must be a string", + }, + args: { + type: 'object', + errorMessage: "The 'args' field must be an object", + }, + bindings: { + type: 'object', + properties: { + edge_storage: { + type: 'array', + items: { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in edge_storage items', + required: "The 'bucket' field is required", + }, + }, + errorMessage: "The 'edge_storage' field must be an array of storage bindings", + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in bindings object', + }, + }, + }, + required: ['name', 'target'], + additionalProperties: false, +}; const schemaFirewallManifest = { type: ['object', 'null'], @@ -559,85 +572,6 @@ const schemaApplicationCacheSettings = { additionalProperties: false, }; -const schemaApplicationOrigins = { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - origin_type: { - type: 'string', - enum: ORIGIN_TYPES, - errorMessage: - "The 'origin_type' field must be one of: single_origin, load_balancer, live_ingest, object_storage.", - }, - origin_protocol_policy: { - type: 'string', - enum: ORIGIN_PROTOCOL_POLICIES, - errorMessage: "The 'origin_protocol_policy' must be one of: preserve, http, https.", - }, - host_header: { - type: 'string', - errorMessage: "The 'host_header' field must be a string.", - }, - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string.", - }, - addresses: { - type: 'array', - items: { - type: 'object', - properties: { - address: { - type: 'string', - errorMessage: "The 'address' field must be a string.", - }, - weight: { - type: 'number', - minimum: 0, - maximum: 100, - errorMessage: "The 'weight' field must be a number between 0 and 100.", - }, - server_role: { - type: 'string', - enum: ['primary', 'backup'], - errorMessage: "The 'server_role' field must be either 'primary' or 'backup'.", - }, - }, - required: ['address'], - additionalProperties: false, - }, - }, - load_balancer: { - type: 'object', - properties: { - method: { - type: 'string', - enum: LOAD_BALANCER_METHODS, - errorMessage: "The 'method' field must be one of: ip_hash, least_connections, round_robin.", - }, - }, - required: ['method'], - additionalProperties: false, - }, - }, - required: ['name', 'origin_type', 'addresses'], - allOf: [ - { - if: { - properties: { origin_type: { const: 'object_storage' } }, - }, - then: { - required: ['bucket'], - errorMessage: "When origin_type is 'object_storage', the 'bucket' field is required.", - }, - }, - ], - additionalProperties: false, -}; - const schemaApplicationRules = { type: 'object', properties: { @@ -719,91 +653,525 @@ const schemaApplicationRules = { additionalProperties: false, }; -// ... resto do arquivo mantido como está ... - const schemaApplicationManifest = { type: 'object', properties: { - main_settings: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + delivery_protocol: { + type: 'string', + enum: APPLICATION_DELIVERY_PROTOCOLS, + default: 'http', + errorMessage: "The 'delivery_protocol' field must be either 'http,https' or 'http'.", + }, + http3: { + type: 'boolean', + errorMessage: "The 'http3' field must be a boolean.", + }, + http_port: { + type: 'array', + items: { + type: 'integer', + enum: APPLICATION_HTTP_PORTS, + }, + errorMessage: { + enum: "The 'http_port' field must be an array of valid HTTP ports.", + type: "The 'http_port' field must be an array", + }, + }, + https_port: { + type: 'array', + items: { + type: 'integer', + enum: APPLICATION_HTTPS_PORTS, + }, + default: [443], + errorMessage: "The 'https_port' field must be an array of valid HTTPS ports.", + }, + minimum_tls_version: { + type: 'string', + enum: APPLICATION_TLS_VERSIONS, + default: '', + errorMessage: "The 'minimum_tls_version' field must be a valid TLS version.", + }, + supported_ciphers: { + type: 'string', + enum: APPLICATION_SUPPORTED_CIPHERS, + default: 'all', + errorMessage: "The 'supported_ciphers' field must be a valid cipher suite.", + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean.", + }, + debug: { + type: 'boolean', + default: false, + errorMessage: "The 'debug' field must be a boolean.", + }, + modules: { type: 'object', properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - delivery_protocol: { - type: 'string', - enum: APPLICATION_DELIVERY_PROTOCOLS, - default: 'http', - errorMessage: "The 'delivery_protocol' field must be either 'http,https' or 'http'.", - }, - http3: { + edge_cache_enabled: { type: 'boolean', - errorMessage: "The 'http3' field must be a boolean.", - }, - http_port: { - type: 'array', - items: { - type: 'integer', - enum: APPLICATION_HTTP_PORTS, - }, - errorMessage: { - enum: "The 'http_port' field must be an array of valid HTTP ports.", - type: "The 'http_port' field must be an array", - }, + default: true, + errorMessage: "The 'edge_cache_enabled' field must be a boolean.", }, - https_port: { - type: 'array', - items: { - type: 'integer', - enum: APPLICATION_HTTPS_PORTS, - }, - default: [443], - errorMessage: "The 'https_port' field must be an array of valid HTTPS ports.", + edge_functions_enabled: { + type: 'boolean', + default: false, + errorMessage: "The 'edge_functions_enabled' field must be a boolean.", }, - minimum_tls_version: { - type: 'string', - enum: APPLICATION_TLS_VERSIONS, - default: '', - errorMessage: "The 'minimum_tls_version' field must be a valid TLS version.", + application_accelerator_enabled: { + type: 'boolean', + default: false, + errorMessage: "The 'application_accelerator_enabled' field must be a boolean.", }, - supported_ciphers: { - type: 'string', - enum: APPLICATION_SUPPORTED_CIPHERS, - default: 'all', - errorMessage: "The 'supported_ciphers' field must be a valid cipher suite.", + image_processor_enabled: { + type: 'boolean', + default: false, + errorMessage: "The 'image_processor_enabled' field must be a boolean.", }, - active: { + tiered_cache_enabled: { type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean.", + default: false, + errorMessage: "The 'tiered_cache_enabled' field must be a boolean.", }, }, + required: [], additionalProperties: false, - required: ['name'], + errorMessage: { + additionalProperties: 'No additional properties are allowed in modules object.', + }, }, cache_settings: { type: 'array', items: schemaApplicationCacheSettings, errorMessage: "The 'cache_settings' field must be an array of cache setting items.", }, - origins: { - type: 'array', - items: schemaApplicationOrigins, - errorMessage: "The 'origins' field must be an array of origin items.", - }, rules: { type: 'array', items: schemaApplicationRules, errorMessage: "The 'rules' field must be an array of application rule items.", }, }, - required: ['main_settings'], + required: ['name'], additionalProperties: false, errorMessage: { additionalProperties: 'No additional properties are allowed in application items.', - required: "The 'name' and 'main_settings' fields are required.", + required: "The 'name field are required.", + }, +}; + +const schemaWorkloadManifest = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 1, + maxLength: 100, + errorMessage: "The 'name' field must be a string between 1 and 100 characters", + }, + alternate_domains: { + type: 'array', + items: { type: 'string' }, + maxItems: 50, + errorMessage: "The 'alternate_domains' field must be an array of strings with max 50 items", + }, + edge_application: { + type: 'string', + errorMessage: "The 'edge_application' field must be a string", + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean", + }, + network_map: { + type: 'string', + enum: WORKLOAD_NETWORK_MAP, + default: '1', + errorMessage: "The 'network_map' must be either '1' or '2'", + }, + edge_firewall: { + type: ['string', 'null'], + errorMessage: "The 'edge_firewall' must be an string or null", + }, + workload_hostname_allow_access: { + type: 'boolean', + default: true, + errorMessage: "The 'workload_hostname_allow_access' field must be a boolean", + }, + domains: { + type: 'array', + items: { + type: 'string', + minLength: 1, + maxLength: 250, + errorMessage: 'Each domain must be a string between 1 and 250 characters', + }, + minItems: 1, + errorMessage: "The 'domains' field must be an array of domain strings", + }, + tls: { + type: 'object', + properties: { + certificate: { + type: ['integer', 'null'], + minimum: 1, + errorMessage: "The 'certificate' must be an integer >= 1 or null", + }, + ciphers: { + type: ['string', 'null'], + enum: [...WORKLOAD_TLS_CIPHERS, null], + errorMessage: "The 'ciphers' must be a valid TLS cipher suite or null", + }, + minimum_version: { + type: ['string', 'null'], + enum: [...WORKLOAD_TLS_VERSIONS, null], + default: 'tls_1_2', + errorMessage: "The 'minimum_version' must be a valid TLS version or null", + }, + }, + default: { certificate: null, ciphers: null, minimum_version: 'tls_1_2' }, + additionalProperties: false, + }, + protocols: { + type: 'object', + properties: { + http: { + type: 'object', + properties: { + versions: { + type: 'array', + items: { + type: 'string', + enum: WORKLOAD_HTTP_VERSIONS, + }, + default: ['http1', 'http2'], + }, + http_ports: { + type: 'array', + items: { type: 'integer' }, + default: [80], + }, + https_ports: { + type: 'array', + items: { type: 'integer' }, + default: [443], + }, + quic_ports: { + type: ['array', 'null'], + items: { type: 'integer' }, + }, + }, + required: ['versions', 'http_ports', 'https_ports'], + additionalProperties: false, + }, + }, + default: { + http: { + versions: ['http1', 'http2'], + http_ports: [80], + https_ports: [443], + quic_ports: null, + }, + }, + additionalProperties: false, + }, + mtls: { + type: 'object', + properties: { + verification: { + type: 'string', + enum: WORKLOAD_MTLS_VERIFICATION, + default: 'enforce', + }, + certificate: { + type: ['integer', 'null'], + minimum: 1, + }, + crl: { + type: ['array', 'null'], + items: { type: 'integer' }, + maxItems: 100, + }, + }, + required: ['verification'], + additionalProperties: false, + }, + }, + required: ['name', 'edge_application', 'domains'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in workload items', + required: "The 'name', 'edge_application' and 'domains' fields are required in workload items", + }, +}; + +const schemaEdgeConnectorManifest = { + type: ['object', 'null'], + properties: { + name: { + type: 'string', + minLength: 1, + maxLength: 255, + errorMessage: "The 'name' field must be a string between 1 and 255 characters", + }, + modules: { + type: 'object', + properties: { + load_balancer_enabled: { + type: 'boolean', + errorMessage: "'load_balancer_enabled' must be a boolean", + }, + origin_shield_enabled: { + type: 'boolean', + errorMessage: "'origin_shield_enabled' must be a boolean", + }, + }, + required: ['load_balancer_enabled', 'origin_shield_enabled'], + additionalProperties: false, + errorMessage: { + required: "'load_balancer_enabled' and 'origin_shield_enabled' are required in modules", + additionalProperties: 'No additional properties are allowed in modules', + }, + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean", + }, + type: { + type: 'string', + enum: EDGE_CONNECTOR_TYPES, + errorMessage: "The 'type' must be one of: http, s3, edge_storage, live_ingest", + }, + type_properties: { + oneOf: [ + { + type: 'object', + properties: { + versions: { + type: 'array', + items: { type: 'string' }, + errorMessage: "The 'versions' field must be an array of strings", + }, + host: { + type: 'string', + errorMessage: "The 'host' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + following_redirect: { + type: 'boolean', + errorMessage: "The 'following_redirect' field must be a boolean", + }, + real_ip_header: { + type: 'string', + errorMessage: "The 'real_ip_header' field must be a string", + }, + real_port_header: { + type: 'string', + errorMessage: "The 'real_port_header' field must be a string", + }, + }, + required: ['versions', 'host', 'path'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in HTTP type properties', + required: "The 'versions', 'host', and 'path' fields are required for HTTP type", + }, + }, + { + type: 'object', + properties: { + endpoint: { + type: 'string', + errorMessage: "The 'endpoint' field must be a string", + }, + }, + required: ['endpoint'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in Live Ingest type properties', + required: "The 'endpoint' field is required for Live Ingest type", + }, + }, + { + type: 'object', + properties: { + host: { + type: 'string', + errorMessage: "The 'host' field must be a string", + }, + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + region: { + type: 'string', + errorMessage: "The 'region' field must be a string", + }, + access_key: { + type: 'string', + errorMessage: "The 'access_key' field must be a string", + }, + secret_key: { + type: 'string', + errorMessage: "The 'secret_key' field must be a string", + }, + }, + required: ['host', 'bucket', 'path', 'region', 'access_key', 'secret_key'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in S3 type properties', + required: 'All fields are required for S3 type', + }, + }, + { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in Storage type properties', + required: "The 'bucket' field is required for Storage type", + }, + }, + ], + }, + addresses: { + type: 'array', + items: { + type: 'object', + properties: { + address: { + type: 'string', + minLength: 1, + maxLength: 255, + errorMessage: "The 'address' field must be a string between 1 and 255 characters", + }, + plain_port: { + type: 'integer', + minimum: 1, + maximum: 65535, + default: 80, + errorMessage: "The 'plain_port' must be between 1 and 65535", + }, + tls_port: { + type: 'integer', + minimum: 1, + maximum: 65535, + default: 443, + errorMessage: "The 'tls_port' must be between 1 and 65535", + }, + server_role: { + type: 'string', + enum: ['primary', 'backup'], + default: 'primary', + errorMessage: "The 'server_role' must be either 'primary' or 'backup'", + }, + weight: { + type: 'integer', + minimum: 0, + maximum: 100, + default: 1, + errorMessage: "The 'weight' must be between 0 and 100", + }, + active: { + type: 'boolean', + default: true, + errorMessage: "The 'active' field must be a boolean", + }, + max_conns: { + type: 'integer', + minimum: 0, + maximum: 1000, + default: 0, + errorMessage: "The 'max_conns' must be between 0 and 1000", + }, + max_fails: { + type: 'integer', + minimum: 1, + maximum: 10, + default: 1, + errorMessage: "The 'max_fails' must be between 1 and 10", + }, + fail_timeout: { + type: 'integer', + minimum: 1, + maximum: 60, + default: 10, + errorMessage: "The 'fail_timeout' must be between 1 and 60", + }, + }, + required: ['address'], + additionalProperties: false, + }, + }, + tls: { + type: 'object', + properties: { + policy: { type: 'string' }, + }, + default: { policy: 'preserve' }, + }, + load_balance_method: { + type: 'string', + enum: EDGE_CONNECTOR_LOAD_BALANCE, + default: 'off', + errorMessage: `The 'load_balance_method' must be one of: ${EDGE_CONNECTOR_LOAD_BALANCE.join(', ')}`, + }, + connection_preference: { + type: 'array', + items: { + type: 'string', + enum: EDGE_CONNECTOR_CONNECTION_PREFERENCE, + errorMessage: `Each connection preference must be one of: ${EDGE_CONNECTOR_CONNECTION_PREFERENCE.join(', ')}`, + }, + maxItems: 2, + default: ['IPv6', 'IPv4'], + errorMessage: "The 'connection_preference' field must be an array with maximum 2 items", + }, + connection_timeout: { + type: 'integer', + minimum: 1, + maximum: 300, + default: 60, + }, + read_write_timeout: { + type: 'integer', + minimum: 1, + maximum: 300, + default: 120, + }, + max_retries: { + type: 'integer', + minimum: 0, + maximum: 10, + }, }, + required: ['name', 'modules', 'product_version', 'type'], + additionalProperties: false, }; const schemaManifest = { @@ -821,23 +1189,41 @@ const schemaManifest = { type: "The 'waf' field must be an array", }, }, - domain: { - ...schemaDomainsManifest, - errorMessage: { - type: "The 'domain' field must be an object", - }, - }, - firewall: { - ...schemaFirewallManifest, + edge_firewall: { + type: 'array', + items: schemaFirewallManifest, errorMessage: { - type: "The 'firewall' field must be an object", + type: "The 'edge_firewall' field must be an array of edge firewall objects", }, }, - application: { + edge_applications: { type: 'array', items: schemaApplicationManifest, - errorMessage: "The 'application' field must be an array of application items.", + errorMessage: "The 'edge_applications' field must be an array of edge application items.", + }, + workloads: { + type: 'array', + items: schemaWorkloadManifest, + errorMessage: "The 'workload' field must be an array of workloads items.", + }, + edge_connectors: { + type: 'array', + items: schemaEdgeConnectorManifest, + errorMessage: { + type: "The 'edge_connectors' field must be an array", + }, + edge_storage: { + type: 'array', + items: schemaStorageManifest, + errorMessage: "The 'edge_storage' field must be an array of edge storage items.", + }, + edge_functions: { + type: 'array', + items: schemaFunctionManifest, + errorMessage: "The 'edge_functions' field must be an array of edge function items.", + }, }, }, }; + export { schemaManifest }; diff --git a/packages/config/src/configProcessor/processConfig/index.test.ts b/packages/config/src/configProcessor/processConfig/index.test.ts index 8cc3a0e7..baa195c9 100644 --- a/packages/config/src/configProcessor/processConfig/index.test.ts +++ b/packages/config/src/configProcessor/processConfig/index.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { processConfig } from '..'; -import { AzionConfig } from '../../types'; +import { AzionConfig } from '../../config/types'; describe('processConfig', () => { describe('Cache and Rules', () => { @@ -9,9 +9,6 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }; expect(processConfig(config)).toEqual( @@ -19,9 +16,6 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }), ); @@ -1248,17 +1242,17 @@ describe('processConfig', () => { expect.objectContaining({ name: 'Example Rule', description: 'This rule redirects all traffic.', - is_active: false, + active: false, }), expect.objectContaining({ name: 'Second Rule', description: '', // Should default to an empty string - is_active: true, + active: true, }), expect.objectContaining({ name: 'Third Rule', description: 'This rule handles home traffic.', - is_active: true, // Should default to true + active: true, // Should default to true }), ]); }); @@ -1281,25 +1275,21 @@ describe('processConfig', () => { expect(result.rules[0]).toEqual( expect.objectContaining({ name: 'First Request Rule', - order: 2, }), ); expect(result.rules[1]).toEqual( expect.objectContaining({ name: 'Second Request Rule', - order: 3, }), ); expect(result.rules[2]).toEqual( expect.objectContaining({ name: 'First Response Rule', - order: 2, }), ); expect(result.rules[3]).toEqual( expect.objectContaining({ name: 'Second Response Rule', - order: 3, }), ); }); diff --git a/packages/config/src/configProcessor/processConfig/index.ts b/packages/config/src/configProcessor/processConfig/index.ts index f64d18f2..bf589f92 100644 --- a/packages/config/src/configProcessor/processConfig/index.ts +++ b/packages/config/src/configProcessor/processConfig/index.ts @@ -1,5 +1,4 @@ import { AzionConfig } from '../../types'; -import convertLegacyConfig from '../helpers/convertLegacyConfig'; import { factoryProcessContext } from '../processStrategy'; import { validateConfig } from '../validateConfig'; @@ -27,9 +26,8 @@ import { validateConfig } from '../validateConfig'; * const payloadCDN = processConfig(config); * console.log(payloadCDN); */ -function processConfig(inputConfig: AzionConfig) { +function processConfig(config: AzionConfig) { /* Converts legacy configuration properties to the new `behavior` format. */ - const config = convertLegacyConfig(inputConfig); validateConfig(config); // eslint-disable-next-line @typescript-eslint/no-explicit-any const payloadCDN: any = {}; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts similarity index 67% rename from packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts rename to packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts index c21fbbac..fb93539b 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts @@ -1,6 +1,6 @@ import { all, create } from 'mathjs'; -import { AzionCache, AzionConfig } from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; +import { AzionCache } from '../../../../types'; +import ProcessConfigStrategy from '../../processConfigStrategy'; const math = create(all); @@ -22,18 +22,16 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { throw new Error(`Expression is not purely mathematical: ${expression}`); }; - transformToManifest(config: AzionConfig) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const payload: any[] = []; - if (!Array.isArray(config?.cache) || config?.cache.length === 0) { - return; + transformToManifest(applicationCache: AzionCache[]) { + if (!Array.isArray(applicationCache) || applicationCache.length === 0) { + return []; } - config?.cache.forEach((cache) => { + + return applicationCache.map((cache) => { const maxAgeSecondsBrowser = cache?.browser ? this.evaluateMathExpression(cache.browser.maxAgeSeconds) : 0; const maxAgeSecondsEdge = cache?.edge ? this.evaluateMathExpression(cache.edge.maxAgeSeconds) : 60; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const cacheSetting: any = { + return { name: cache.name, browser_cache_settings: cache?.browser ? 'override' : 'honor', browser_cache_settings_maximum_ttl: maxAgeSecondsBrowser, @@ -43,39 +41,16 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { enable_caching_for_options: cache?.methods?.options || false, enable_query_string_sort: cache?.queryStringSort || false, }; - - if (cache.cacheByQueryString) { - cacheSetting.cache_by_query_string = - cache.cacheByQueryString.option === 'varies' ? 'all' : cache.cacheByQueryString.option; - if (cache.cacheByQueryString.option === 'whitelist' || cache.cacheByQueryString.option === 'blacklist') { - cacheSetting.query_string_fields = cache.cacheByQueryString.list || []; - } else { - cacheSetting.query_string_fields = []; - } - } - - if (cache.cacheByCookie) { - cacheSetting.cache_by_cookie = cache.cacheByCookie.option === 'varies' ? 'all' : cache.cacheByCookie.option; - if (cache.cacheByCookie.option === 'whitelist' || cache.cacheByCookie.option === 'blacklist') { - cacheSetting.cookie_names = cache.cacheByCookie.list || []; - } else { - cacheSetting.cookie_names = []; - } - } - - payload.push(cacheSetting); }); - return payload; } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { - const config = payload.cache; - if (!Array.isArray(config) || config.length === 0) { - return; + transformToConfig(cacheSettings: any[]) { + if (!Array.isArray(cacheSettings) || cacheSettings.length === 0) { + return []; } - transformedPayload.cache = []; - config.forEach((cache) => { + + return cacheSettings.map((cache) => { const maxAgeSecondsBrowser = cache.browser_cache_settings_maximum_ttl ? this.evaluateMathExpression(cache.browser_cache_settings_maximum_ttl) : 0; @@ -134,9 +109,8 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { } } - transformedPayload.cache!.push(cacheSetting); + return cacheSetting; }); - return transformedPayload.cache; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts new file mode 100644 index 00000000..fa68d78a --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts @@ -0,0 +1,73 @@ +import { AzionConfig, AzionEdgeApplication } from '../../../../types'; +import ProcessConfigStrategy from '../../processConfigStrategy'; +import CacheProcessConfigStrategy from './cacheProcessConfigStrategy'; +import RulesProcessConfigStrategy from './rulesProcessConfigStrategy'; + +class EdgeApplicationProcessConfigStrategy extends ProcessConfigStrategy { + private cacheStrategy = new CacheProcessConfigStrategy(); + private rulesStrategy = new RulesProcessConfigStrategy(); + + transformToManifest(config: AzionConfig) { + if (!config.edgeApplications || !Array.isArray(config.edgeApplications)) { + return []; + } + + return config.edgeApplications.map((app: AzionEdgeApplication) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const application: Record = { + name: app.name, + active: app.active ?? true, + debug: app.debug ?? false, + modules: { + edge_cache_enabled: app.edgeCacheEnabled ?? true, + edge_functions_enabled: app.edgeFunctionsEnabled ?? false, + application_accelerator_enabled: app.applicationAcceleratorEnabled ?? false, + image_processor_enabled: app.imageProcessorEnabled ?? false, + tiered_cache_enabled: app.tieredCacheEnabled ?? false, + }, + }; + + if (app.cache) { + application.cache_settings = this.cacheStrategy.transformToManifest(app.cache); + } + + if (app.rules) { + application.rules = this.rulesStrategy.transformToManifest( + app.rules, + config.edgeFunctions, + config.edgeConnectors, + ); + } + + return application; + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: any, transformedPayload: AzionConfig) { + if (!payload.edgeApplications || !Array.isArray(payload.edgeApplications)) { + transformedPayload.edgeApplications = []; + return transformedPayload.edgeApplications; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformedPayload.edgeApplications = payload.edgeApplications.map((app: any) => { + return { + name: app.name, + active: app.active, + debug: app.debug, + edgeCacheEnabled: app.edge_cache_enabled, + edgeFunctionsEnabled: app.edge_functions_enabled, + applicationAcceleratorEnabled: app.application_accelerator_enabled, + imageProcessorEnabled: app.image_processor_enabled, + tieredCacheEnabled: app.tiered_cache_enabled, + cache: app.cache_settings ? this.cacheStrategy.transformToConfig(app.cache_settings) : undefined, + rules: app.rules ? this.rulesStrategy.transformToConfig(app.rules, transformedPayload) : undefined, + }; + }); + + return transformedPayload.edgeApplications; + } +} + +export default EdgeApplicationProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts similarity index 79% rename from packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts rename to packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts index 21c8da4f..0b51d77b 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts @@ -1,11 +1,17 @@ -import { AzionConfig, AzionFirewallCriteriaWithValue } from '../../../types'; +import { + AzionConfig, + AzionEdgeConnector, + AzionEdgeFirewallCriteriaWithValue, + AzionEdgeFunction, + AzionRules, +} from '../../../../types'; import { requestBehaviors, responseBehaviors, revertRequestBehaviors, revertResponseBehaviors, -} from '../../helpers/behaviors'; -import ProcessConfigStrategy from '../processConfigStrategy'; +} from '../../../helpers/behaviors'; +import ProcessConfigStrategy from '../../processConfigStrategy'; /** * RulesProcessConfigStrategy @@ -39,14 +45,14 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } } - private validateFunctionReferences(config: AzionConfig) { - if (!config?.rules?.request || !config?.functions) { + private validateFunctionReferences(applicationRules: AzionRules, functions?: AzionEdgeFunction[]) { + if (!applicationRules?.request || !functions) { return; } - const definedFunctions = new Set(config.functions.map((f) => f.name)); + const definedFunctions = new Set(functions.map((f) => f.name)); - for (const rule of config.rules.request) { + for (const rule of applicationRules.request) { if (rule.behavior?.runFunction) { if (!definedFunctions.has(rule.behavior.runFunction)) { throw new Error( @@ -58,29 +64,34 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToManifest(config: AzionConfig, context: any) { - // Validar referências de funções antes de transformar - this.validateFunctionReferences(config); + transformToManifest( + applicationRules: AzionRules, + functions?: AzionEdgeFunction[], + edgeConnectors?: AzionEdgeConnector[], + ) { + // Validar referências de funções + this.validateFunctionReferences(applicationRules, functions); // eslint-disable-next-line @typescript-eslint/no-explicit-any const payload: any[] = []; - if (config?.rules === undefined || Object.keys(config.rules).length === 0) { + if (!applicationRules || Object.keys(applicationRules).length === 0) { return; } + // request - if (Array.isArray(config?.rules?.request)) { - config?.rules?.request?.forEach((rule, index) => { + if (Array.isArray(applicationRules?.request)) { + applicationRules.request.forEach((rule) => { const cdnRule = { name: rule.name, phase: 'request', description: rule.description ?? '', - is_active: rule.active !== undefined ? rule.active : true, // Default to true if not provided - order: index + 2, // index starts at 2, because the default rule is index 1 + active: rule.active !== undefined ? rule.active : true, // Default to true if not provided + criteria: rule.criteria ? [ rule.criteria.map((criterion) => { const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; + const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, @@ -100,25 +111,24 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { ], behaviors: [], }; - this.addBehaviors(cdnRule, rule.behavior, requestBehaviors, context); + this.addBehaviors(cdnRule, rule.behavior, requestBehaviors, { edgeConnectors }); payload.push(cdnRule); }); } // response - if (Array.isArray(config?.rules?.response)) { - config?.rules?.response.forEach((rule, index) => { + if (Array.isArray(applicationRules?.response)) { + applicationRules.response.forEach((rule) => { const cdnRule = { name: rule.name, phase: 'response', description: rule.description ?? '', - is_active: rule.active !== undefined ? rule.active : true, // Default to true if not provided - order: index + 2, // index starts at 2, because the default rule is index 1 + active: rule.active !== undefined ? rule.active : true, // Default to true if not provided criteria: rule.criteria ? [ rule.criteria.map((criterion) => { const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; + const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, @@ -138,7 +148,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { ], behaviors: [], }; - this.addBehaviors(cdnRule, rule.behavior, responseBehaviors, context); + this.addBehaviors(cdnRule, rule.behavior, responseBehaviors, { edgeConnectors }); payload.push(cdnRule); }); } @@ -147,7 +157,16 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { + transformToConfig(rulesPayload: any[], transformedPayload: AzionConfig) { + if (!Array.isArray(rulesPayload)) { + return undefined; + } + + const rules: AzionRules = { + request: [], + response: [], + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const addBehaviorsObject = (behaviors: any, behaviorDefinitions: any, context: any) => { if (behaviors && Array.isArray(behaviors)) { @@ -177,23 +196,13 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { return undefined; }; - const rulesConfig = payload.rules; - if (!rulesConfig || Object.keys(rulesConfig).length === 0) { - return; - } - - transformedPayload.rules = { - request: [], - response: [], - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - rulesConfig.forEach((rule: any) => { + rulesPayload.forEach((rule: any) => { if (rule.phase === 'request') { - transformedPayload.rules!.request!.push({ + rules.request!.push({ name: rule.name, description: rule.description, - active: rule.is_active, + active: rule.active, criteria: // Verifica se criteria existe e é um array de arrays Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) @@ -210,10 +219,10 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { behavior: addBehaviorsObject(rule.behaviors, revertRequestBehaviors, transformedPayload), }); } else if (rule.phase === 'response') { - transformedPayload.rules!.response!.push({ + rules.response!.push({ name: rule.name, description: rule.description, - active: rule.is_active, + active: rule.active, criteria: Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) ? // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -231,7 +240,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } }); - return transformedPayload.rules; + return rules; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts deleted file mode 100644 index c7a1eee6..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { AzionConfig } from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; - -/** - * DomainProcessConfigStrategy - * @class DomainProcessConfigStrategy - * @description This class is implementation of the Domain ProcessConfig Strategy. - */ -class DomainProcessConfigStrategy extends ProcessConfigStrategy { - transformToManifest(config: AzionConfig) { - const domain = config?.domain; - if (!domain || Object.keys(domain).length === 0) { - return; - } - if ( - domain.digitalCertificateId && - typeof domain.digitalCertificateId === 'string' && - domain.digitalCertificateId !== 'lets_encrypt' - ) { - throw new Error( - `Domain ${domain.name} has an invalid digital certificate ID: ${domain.digitalCertificateId}. Only 'lets_encrypt' or null is supported.`, - ); - } - - if ( - domain.mtls?.verification && - domain.mtls.verification !== 'enforce' && - domain.mtls.verification !== 'permissive' - ) { - throw new Error( - `Domain ${domain.name} has an invalid verification value: ${domain.mtls.verification}. Only 'enforce' or 'permissive' is supported.`, - ); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const domainSetting: any = { - name: domain.name, - cname_access_only: domain.cnameAccessOnly ?? false, - cnames: domain.cnames || [], - digital_certificate_id: domain.digitalCertificateId || null, - edge_application_id: domain.edgeApplicationId || null, - edge_firewall_id: domain.edgeFirewallId || null, - is_active: domain.active === undefined ? true : domain.active, - }; - if (domain.mtls) { - domainSetting.is_mtls_enabled = true; - domainSetting.mtls_verification = domain.mtls.verification; - domainSetting.mtls_trusted_ca_certificate_id = domain.mtls.trustedCaCertificateId; - domainSetting.crl_list = domain.mtls.crlList || []; - } else { - domainSetting.is_mtls_enabled = false; - } - return domainSetting; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { - const domain = payload.domain; - if (!domain || Object.keys(domain).length === 0) { - return; - } - transformedPayload.domain = { - name: domain.name, - cnameAccessOnly: domain.cname_access_only, - cnames: domain.cnames, - digitalCertificateId: domain.digital_certificate_id, - edgeApplicationId: domain.edge_application_id, - edgeFirewallId: domain.edge_firewall_id, - active: domain.is_active === undefined ? true : domain.is_active, - mtls: domain.mtls_verification - ? { - verification: domain.mtls_verification, - trustedCaCertificateId: domain.mtls_trusted_ca_certificate_id, - crlList: domain.crl_list, - } - : undefined, - }; - return transformedPayload.domain; - } -} - -export default DomainProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts new file mode 100644 index 00000000..cf081470 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts @@ -0,0 +1,248 @@ +import { + EDGE_CONNECTOR_ALLOWED_PROPERTIES, + EDGE_CONNECTOR_CONNECTION_PREFERENCE, + EDGE_CONNECTOR_LOAD_BALANCE, + EDGE_CONNECTOR_SERVER_ROLE, +} from '../../../constants'; +import { + AzionConfig, + AzionEdgeConnector, + EdgeConnectorType, + EdgeConnectorTypeProperty, + HttpTypeProperty, + LiveIngestTypeProperty, + S3TypeProperty, + StorageTypeProperty, +} from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +class EdgeConnectorProcessConfigStrategy extends ProcessConfigStrategy { + transformToManifest(config: AzionConfig) { + const edgeConnectors = config?.edgeConnectors; + if (!edgeConnectors || edgeConnectors.length === 0) { + return; + } + + return edgeConnectors.map((connector: AzionEdgeConnector) => ({ + name: connector.name, + modules: { + load_balancer_enabled: connector.modules.loadBalancerEnabled, + origin_shield_enabled: connector.modules.originShieldEnabled, + }, + active: connector.active ?? true, + type: connector.type, + type_properties: this.transformTypePropertiesToSnakeCase(connector.type, connector.typeProperties), + addresses: connector.addresses?.map((addr) => ({ + address: addr.address, + plain_port: addr.plainPort ?? 80, + tls_port: addr.tlsPort ?? 443, + server_role: addr.serverRole ?? 'primary', + weight: addr.weight ?? 1, + active: addr.active ?? true, + max_conns: addr.maxConns ?? 0, + max_fails: addr.maxFails ?? 1, + fail_timeout: addr.failTimeout ?? 10, + })), + tls: connector.tls ?? { policy: 'preserve' }, + load_balance_method: connector.loadBalanceMethod ?? 'off', + connection_preference: connector.connectionPreference ?? ['IPv6', 'IPv4'], + connection_timeout: connector.connectionTimeout ?? 60, + read_write_timeout: connector.readWriteTimeout ?? 120, + max_retries: connector.maxRetries ?? 0, + })); + } + + private transformTypePropertiesToSnakeCase(type: EdgeConnectorType, properties: EdgeConnectorTypeProperty) { + if (!properties) { + throw new Error(`Edge Connector of type '${type}' requires 'typeProperties'. Please add the required properties: + - For type 'http': { versions: string[], host: string, path: string } + - For type 'live_ingest': { endpoint: string } + - For type 's3': { host: string, bucket: string, path: string, region: string, accessKey: string, secretKey: string } + - For type 'edge_storage': { bucket: string }`); + } + + switch (type) { + case 'http': { + const httpProps = properties as HttpTypeProperty; + return { + versions: httpProps.versions, + host: httpProps.host, + path: httpProps.path, + following_redirect: httpProps.followingRedirect, + real_ip_header: httpProps.realIpHeader, + real_port_header: httpProps.realPortHeader, + }; + } + case 'live_ingest': { + const liveProps = properties as LiveIngestTypeProperty; + return { + endpoint: liveProps.endpoint, + }; + } + case 's3': { + const s3Props = properties as S3TypeProperty; + return { + host: s3Props.host, + bucket: s3Props.bucket, + path: s3Props.path, + region: s3Props.region, + access_key: s3Props.accessKey, + secret_key: s3Props.secretKey, + }; + } + case 'edge_storage': { + const storageProps = properties as StorageTypeProperty; + return { + bucket: storageProps.bucket, + prefix: storageProps.prefix, + }; + } + default: + throw new Error(`Invalid Edge Connector type: ${type}`); + } + } + + private validateTypeProperties(type: EdgeConnectorType, properties: EdgeConnectorTypeProperty) { + // Check if there are any invalid properties for the type + const invalidProperties = Object.keys(properties).filter( + (prop) => !(EDGE_CONNECTOR_ALLOWED_PROPERTIES[type] as unknown as string[]).includes(prop), + ); + if (invalidProperties.length > 0) { + throw new Error(`Invalid properties for type ${type}: ${invalidProperties.join(', ')}`); + } + } + + transformToConfig( + payload: { + edge_connector?: Array<{ + name: string; + modules: { load_balancer_enabled: boolean; origin_shield_enabled: boolean }; + active?: boolean; + type: EdgeConnectorType; + type_properties: Record; + addresses?: Array<{ + address: string; + plain_port?: number; + tls_port?: number; + server_role?: string; + weight?: number; + active?: boolean; + max_conns?: number; + max_fails?: number; + fail_timeout?: number; + }>; + tls?: { policy: string }; + load_balance_method?: string; + connection_preference?: string[]; + connection_timeout?: number; + read_write_timeout?: number; + max_retries?: number; + }>; + }, + transformedPayload: AzionConfig, + ) { + if (!payload.edge_connector || payload.edge_connector.length === 0) { + return; + } + + transformedPayload.edgeConnectors = payload.edge_connector.map( + (connector: { + name: string; + modules: { load_balancer_enabled: boolean; origin_shield_enabled: boolean }; + active?: boolean; + type: EdgeConnectorType; + type_properties: Record; + addresses?: Array<{ + address: string; + plain_port?: number; + tls_port?: number; + server_role?: string; + weight?: number; + active?: boolean; + max_conns?: number; + max_fails?: number; + fail_timeout?: number; + }>; + tls?: { policy: string }; + load_balance_method?: string; + connection_preference?: string[]; + connection_timeout?: number; + read_write_timeout?: number; + max_retries?: number; + }) => { + const typeProperties = this.transformTypePropertiesFromSnakeCase(connector.type, connector.type_properties); + this.validateTypeProperties(connector.type, typeProperties); + + return { + name: connector.name, + modules: { + loadBalancerEnabled: connector.modules.load_balancer_enabled, + originShieldEnabled: connector.modules.origin_shield_enabled, + }, + active: connector.active, + type: connector.type, + typeProperties, + addresses: connector.addresses?.map((addr) => ({ + address: addr.address, + plainPort: addr.plain_port, + tlsPort: addr.tls_port, + serverRole: addr.server_role as (typeof EDGE_CONNECTOR_SERVER_ROLE)[number], + weight: addr.weight, + active: addr.active, + maxConns: addr.max_conns, + maxFails: addr.max_fails, + failTimeout: addr.fail_timeout, + })), + tls: connector.tls, + loadBalanceMethod: connector.load_balance_method as (typeof EDGE_CONNECTOR_LOAD_BALANCE)[number], + connectionPreference: + connector.connection_preference as (typeof EDGE_CONNECTOR_CONNECTION_PREFERENCE)[number][], + connectionTimeout: connector.connection_timeout, + readWriteTimeout: connector.read_write_timeout, + maxRetries: connector.max_retries, + }; + }, + ); + + return transformedPayload.edgeConnectors; + } + + private transformTypePropertiesFromSnakeCase( + type: EdgeConnectorType, + properties: Record, + ): EdgeConnectorTypeProperty { + switch (type) { + case 'http': + return { + versions: properties.versions as string[], + host: properties.host as string, + path: properties.path as string, + followingRedirect: properties.following_redirect as boolean, + realIpHeader: properties.real_ip_header as string, + realPortHeader: properties.real_port_header as string, + } as HttpTypeProperty; + case 'live_ingest': + return { + endpoint: properties.endpoint as string, + } as LiveIngestTypeProperty; + case 's3': + return { + host: properties.host as string, + bucket: properties.bucket as string, + path: properties.path as string, + region: properties.region as string, + accessKey: properties.access_key as string, + secretKey: properties.secret_key as string, + } as S3TypeProperty; + case 'edge_storage': + return { + bucket: properties.bucket as string, + prefix: properties.prefix as string, + } as StorageTypeProperty; + default: + throw new Error(`Invalid Edge Connector type: ${type}`); + } + } +} + +export default EdgeConnectorProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts index 1168428a..9a075dc4 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts @@ -7,31 +7,76 @@ import ProcessConfigStrategy from '../processConfigStrategy'; * @description This class is implementation of the Functions ProcessConfig Strategy. */ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { + private validateStorageBinding(config: AzionConfig, bucketName: string, functionName: string) { + if (!Array.isArray(config?.edgeStorage) || !config.edgeStorage.find((storage) => storage.name === bucketName)) { + throw new Error( + `Function "${functionName}" references storage bucket "${bucketName}" which is not defined in the storage configuration.`, + ); + } + } + transformToManifest(config: AzionConfig) { - if (!Array.isArray(config?.functions) || config?.functions.length === 0) { - return; + if (!Array.isArray(config?.edgeFunctions) || config?.edgeFunctions.length === 0) { + return {}; } - return config.functions.map((func) => ({ - name: func.name, - target: func.path, - args: func.args || {}, - })); + return config.edgeFunctions.map((func) => { + // Validar se o bucket referenciado existe + if (func.bindings?.storage?.bucket) { + this.validateStorageBinding(config, func.bindings.storage.bucket, func.name); + } + + return { + name: func.name, + target: func.path, + args: func.args || {}, + bindings: func.bindings + ? { + edge_storage: func.bindings.storage + ? { + bucket: func.bindings.storage.bucket, + prefix: func.bindings.storage.prefix, + } + : undefined, + } + : undefined, + }; + }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any transformToConfig(payload: any, transformedPayload: AzionConfig) { - if (!Array.isArray(payload?.functions) || payload?.functions.length === 0) { + if (!Array.isArray(payload?.edgeFunction) || payload?.edgeFunction.length === 0) { return; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformedPayload.functions = payload.functions.map((func: any) => ({ - name: func.name, - path: func.target, - args: func.args || {}, - })); + transformedPayload.edgeFunctions = payload.functions.map((func: any) => { + const config = { + name: func.name, + path: func.target, + args: func.args || {}, + bindings: func.bindings + ? { + storage: func.bindings.edge_storage + ? { + bucket: func.bindings.edge_storage.bucket, + prefix: func.bindings.edge_storage.prefix, + } + : undefined, + } + : undefined, + }; + + // Validar se o bucket referenciado existe + if (config.bindings?.storage?.bucket) { + this.validateStorageBinding(transformedPayload, config.bindings.storage.bucket, config.name); + } + + return config; + }); - return transformedPayload.functions; + return transformedPayload.edgeFunctions; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts deleted file mode 100644 index 4054ecb2..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import ProcessConfigStrategy from '../processConfigStrategy'; - -export type LocalConfig = { - version?: string; - environment?: string; - timestamp?: string; - // outras configurações que quisermos adicionar -}; - -class LocalProcessConfigStrategy extends ProcessConfigStrategy { - private localConfig: LocalConfig; - - constructor(localConfig?: LocalConfig) { - super(); - this.localConfig = { - version: localConfig?.version || '1.0.0', - environment: localConfig?.environment || process.env.NODE_ENV || 'development', - timestamp: localConfig?.timestamp || new Date().toISOString(), - }; - } - - transformToManifest(_config: unknown, transformedPayload: any) { - transformedPayload.local = this.localConfig; - return transformedPayload.local; - } - - transformToConfig() { - return; - } -} - -export default LocalProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts deleted file mode 100644 index ea9ec172..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { AzionConfig, AzionOrigin } from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; - -/** - * OriginProcessConfigStrategy - * @class OriginProcessConfigStrategy - * @description This class is implementation of the Origin ProcessConfig Strategy. - */ -class OriginProcessConfigStrategy extends ProcessConfigStrategy { - transformToManifest(config: AzionConfig) { - const payload: AzionOrigin[] = []; - if (!Array.isArray(config?.origin) || config?.origin.length === 0) { - return; - } - const originsType = ['single_origin', 'object_storage', 'load_balancer', 'live_ingest']; - config?.origin.forEach((origin) => { - if (originsType.indexOf(origin.type) === -1) { - throw new Error(`Rule setOrigin originType '${origin.type}' is not supported`); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originSetting: any = { - id: origin.id, - key: origin.key, - name: origin.name, - origin_type: origin.type, - }; - - if (origin.type !== 'object_storage') { - if (origin.path === '/') { - throw new Error('Origin path cannot be "/". Please use empty string or "/path"'); - } - originSetting.origin_path = origin.path || ''; - originSetting.origin_protocol_policy = origin.protocolPolicy || 'preserve'; - originSetting.method = origin.method || 'ip_hash'; - originSetting.is_origin_redirection_enabled = origin.redirection ?? false; - originSetting.connection_timeout = origin.connectionTimeout || 60; - originSetting.timeout_between_bytes = origin.timeoutBetweenBytes || 120; - - if (origin.addresses && origin.addresses.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const addresses: any[] = []; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - origin?.addresses.forEach((address: any) => { - if (typeof address === 'string') { - addresses.push({ - address, - }); - return; - } - if (address?.weight < 0 || address?.weight > 10) { - throw new Error(`When origin type is ${origin.type}, weight must be between 0 and 10`); - } - addresses.push(address); - }); - originSetting.addresses = addresses; - } else { - throw new Error(`When origin type is ${origin.type}, addresses is required`); - } - - originSetting.host_header = origin.hostHeader || '${host}'; - if (origin?.hmac) { - originSetting.hmac_authentication = true; - originSetting.hmac_region_name = origin.hmac?.region; - originSetting.hmac_access_key = origin.hmac?.accessKey; - originSetting.hmac_secret_key = origin.hmac?.secretKey; - } - } else if (origin.type === 'object_storage') { - originSetting.bucket = origin.bucket; - originSetting.prefix = origin.prefix || ''; - } - - payload.push(originSetting); - }); - return payload; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { - const config = payload.origin; - if (!Array.isArray(config) || config.length === 0) { - return; - } - transformedPayload.origin = []; - const originsType = ['single_origin', 'object_storage', 'load_balancer', 'live_ingest']; - config.forEach((origin) => { - if (originsType.indexOf(origin.origin_type) === -1) { - throw new Error(`originType '${origin.origin_type}' is not supported`); - } - const originSetting: AzionOrigin = { - id: origin.id, - key: origin.key, - name: origin.name, - type: origin.origin_type, - }; - - if (originSetting.type !== 'object_storage') { - if (origin.path === '/') { - throw new Error('Origin path cannot be "/". Please use empty string or "/path"'); - } - originSetting.path = origin.origin_path; - originSetting.protocolPolicy = origin.origin_protocol_policy; - originSetting.method = origin.method; - originSetting.redirection = origin.is_origin_redirection_enabled ?? false; - originSetting.connectionTimeout = origin.connection_timeout; - originSetting.timeoutBetweenBytes = origin.timeout_between_bytes; - originSetting.addresses = origin.addresses; - originSetting.hostHeader = origin.host_header; - if (origin.hmac_authentication) { - originSetting.hmac = { - region: origin.hmac_region_name, - accessKey: origin.hmac_access_key, - secretKey: origin.hmac_secret_key, - }; - } - } else if (originSetting.type === 'object_storage') { - originSetting.bucket = origin.bucket; - originSetting.prefix = origin.prefix; - } - - transformedPayload.origin!.push(originSetting); - }); - return transformedPayload.origin; - } -} - -export default OriginProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts index a9585d9e..9f9c50e9 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts @@ -11,171 +11,111 @@ describe('FirewallProcessConfigStrategy', () => { describe('transformToManifest', () => { it('should transform a complete firewall config to manifest', () => { const config: AzionConfig = { - firewall: { - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: true, - networkProtection: true, - waf: true, - debugRules: true, - rules: [ - { - name: 'Test Rule', - description: 'Test Description', - active: true, - match: '/test', - variable: 'uri', - criteria: [ - { - variable: 'uri', - conditional: 'if', - operator: 'matches', - inputValue: '/test', - }, - ], - behavior: { - deny: true, - }, - }, - ], - }, - }; - - const manifest = strategy.transformToManifest(config); - expect(manifest).toEqual({ - main_settings: { - name: 'Test Firewall', - domains: ['example.com'], - is_active: true, - edge_functions_enabled: true, - network_protection_enabled: true, - waf_enabled: true, - debug_rules: true, - }, - rules_engine: [ + edgeFirewall: [ { - name: 'Test Rule', - description: 'Test Description', - is_active: true, - behaviors: [{ name: 'deny', target: '' }], - criteria: [ - [ - { - variable: 'uri', - conditional: 'if', - operator: 'matches', - input_value: '/test', + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: true, + networkProtection: true, + waf: true, + debugRules: true, + rules: [ + { + name: 'Test Rule', + description: 'Test Description', + active: true, + match: '/test', + variable: 'uri', + criteria: [ + { + variable: 'uri', + conditional: 'if', + operator: 'matches', + inputValue: '/test', + }, + ], + behavior: { + deny: true, }, - ], + }, ], }, ], - }); - }); - - it('should handle firewall config without rules', () => { - const config: AzionConfig = { - firewall: { - name: 'Test Firewall', - domains: ['example.com'], - active: true, - }, }; const manifest = strategy.transformToManifest(config); - expect(manifest).toEqual({ - main_settings: { - name: 'Test Firewall', - domains: ['example.com'], - is_active: true, - edge_functions_enabled: false, - network_protection_enabled: false, - waf_enabled: false, - debug_rules: false, + expect(manifest).toEqual([ + { + main_settings: { + name: 'Test Firewall', + domains: ['example.com'], + is_active: true, + edge_functions_enabled: true, + network_protection_enabled: true, + waf_enabled: true, + debug_rules: true, + }, + rules_engine: [ + { + name: 'Test Rule', + description: 'Test Description', + is_active: true, + behaviors: [{ name: 'deny', target: '' }], + criteria: [ + [ + { + variable: 'uri', + conditional: 'if', + operator: 'matches', + input_value: '/test', + }, + ], + ], + }, + ], }, - }); - }); - - it('should return empty object when no firewall config is provided', () => { - const config: AzionConfig = {}; - const manifest = strategy.transformToManifest(config); - expect(manifest).toBeUndefined(); + ]); }); - it('should transform all behavior types correctly', () => { + it('should handle firewall config without rules', () => { const config: AzionConfig = { - functions: [ + edgeFirewall: [ { - name: 'function1', - path: '.edge/functions/function1.js', + name: 'Test Firewall', + domains: ['example.com'], + active: true, }, ], - firewall: { - name: 'Test Firewall', - rules: [ - { - name: 'All Behaviors Rule', - active: true, - behavior: { - runFunction: 'function1', - setWafRuleset: { - wafMode: 'learning', - wafId: '123', - }, - setRateLimit: { - type: 'second', - limitBy: 'clientIp', - averageRateLimit: '1000', - maximumBurstSize: '1000', - }, - setCustomResponse: { - statusCode: 403, - contentType: 'text/plain', - contentBody: 'Blocked', - }, - }, - }, - ], - }, }; const manifest = strategy.transformToManifest(config); - expect(manifest.rules_engine[0].behaviors).toEqual([ - { - name: 'run_function', - target: 'function1', - }, - { - name: 'set_waf_ruleset', - target: { - mode: 'learning', - waf_id: '123', - }, - }, + expect(manifest).toEqual([ { - name: 'set_rate_limit', - target: { - type: 'second', - limit_by: 'clientIp', - }, - }, - { - name: 'set_custom_response', - target: { - status_code: 403, - content_type: 'text/plain', - content_body: 'Blocked', + main_settings: { + name: 'Test Firewall', + domains: ['example.com'], + is_active: true, + edge_functions_enabled: false, + network_protection_enabled: false, + waf_enabled: false, + debug_rules: false, }, }, ]); }); + + it('should return undefined when no firewall config is provided', () => { + const config: AzionConfig = {}; + const manifest = strategy.transformToManifest(config); + expect(manifest).toBeUndefined(); + }); }); describe('transformToConfig', () => { it('should transform a complete manifest to config', () => { - const manifest = { - firewall: { + const manifest = [ + { main_settings: { name: 'Test Firewall', domains: ['example.com'], @@ -204,62 +144,66 @@ describe('FirewallProcessConfigStrategy', () => { }, ], }, - }; + ]; const config = {}; - const result = strategy.transformToConfig(manifest, config); + const result = strategy.transformToConfig({ edgeFirewall: manifest }, config); - expect(result).toEqual({ - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: true, - networkProtection: true, - waf: true, - debugRules: true, - rules: [ - { - name: 'Test Rule', - description: 'Test Description', - active: true, - criteria: [ - { - variable: '${uri}', - conditional: 'if', - operator: 'matches', - inputValue: '/test', + expect(result).toEqual([ + { + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: true, + networkProtection: true, + waf: true, + debugRules: true, + rules: [ + { + name: 'Test Rule', + description: 'Test Description', + active: true, + criteria: [ + { + variable: '${uri}', + conditional: 'if', + operator: 'matches', + inputValue: '/test', + }, + ], + behavior: { + deny: true, }, - ], - behavior: { - deny: true, }, - }, - ], - }); + ], + }, + ]); }); it('should handle manifest without rules', () => { - const manifest = { - firewall: { + const manifest = [ + { main_settings: { name: 'Test Firewall', domains: ['example.com'], is_active: true, }, }, - }; + ]; const config = {}; - const result = strategy.transformToConfig(manifest, config); - expect(result).toEqual({ - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: false, - networkProtection: false, - waf: false, - debugRules: false, - }); + const result = strategy.transformToConfig({ edgeFirewall: manifest }, config); + expect(result).toEqual([ + { + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: false, + networkProtection: false, + waf: false, + debugRules: false, + }, + ]); }); it('should return undefined when no firewall manifest is provided', () => { @@ -268,116 +212,5 @@ describe('FirewallProcessConfigStrategy', () => { const result = strategy.transformToConfig(manifest, config); expect(result).toStrictEqual(expect.objectContaining({})); }); - - it('should transform all behavior types from manifest to config', () => { - const manifest = { - firewall: { - name: 'Test Firewall', - rules_engine: [ - { - name: 'All Behaviors Rule', - is_active: true, - behaviors: [ - { - name: 'run_function', - target: 'function1', - }, - { - name: 'set_waf_ruleset', - target: { - mode: 'learning', - waf_id: '123', - }, - }, - { - name: 'set_rate_limit', - target: { - type: 'second', - value: '10', - limit_by: 'ip', - }, - }, - { - name: 'set_custom_response', - target: { - status_code: 403, - content_type: 'text/plain', - content_body: 'Blocked', - }, - }, - ], - }, - ], - }, - }; - - const config = {}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const result: any = strategy.transformToConfig(manifest, config); - expect(result?.rules?.[0].behavior).toEqual({ - runFunction: { - path: 'function1', - }, - setWafRuleset: { - wafMode: 'learning', - wafId: '123', - }, - setRateLimit: { - type: 'second', - value: '10', - limitBy: 'ip', - }, - setCustomResponse: { - statusCode: 403, - contentType: 'text/plain', - contentBody: 'Blocked', - }, - }); - }); - - it('should handle empty behaviors array', () => { - const manifest = { - firewall: { - name: 'Test Firewall', - rules_engine: [ - { - name: 'Empty Behaviors Rule', - is_active: true, - behaviors: [], - }, - ], - }, - }; - - const config = {}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const result: any = strategy.transformToConfig(manifest, config); - expect(result?.rules?.[0].behavior).toEqual({}); - }); - - it('should handle unknown behavior types gracefully', () => { - const manifest = { - firewall: { - name: 'Test Firewall', - rules_engine: [ - { - name: 'Unknown Behavior Rule', - is_active: true, - behaviors: [ - { - name: 'unknown_behavior', - target: 'test', - }, - ], - }, - ], - }, - }; - - const config = {}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const result: any = strategy.transformToConfig(manifest, config); - expect(result?.rules?.[0].behavior).toEqual({}); - }); }); }); diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts index 3c499192..c8a839ca 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts @@ -1,6 +1,10 @@ -import { AzionConfig, AzionFirewall, AzionFirewallCriteriaWithValue, AzionFirewallRule } from '../../../../types'; +import { + AzionConfig, + AzionEdgeFirewall, + AzionEdgeFirewallCriteriaWithValue, + AzionEdgeFirewallRule, +} from '../../../../types'; import ProcessConfigStrategy from '../../processConfigStrategy'; - /** * FirewallProcessConfigStrategy * @class FirewallProcessConfigStrategy @@ -8,56 +12,58 @@ import ProcessConfigStrategy from '../../processConfigStrategy'; */ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { transformToManifest(config: AzionConfig) { - const firewall = config?.firewall; - if (!firewall || Object.keys(firewall).length === 0) { + const firewalls = config?.edgeFirewall; + if (!firewalls || firewalls.length === 0) { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const payload: any = { - main_settings: { - name: firewall.name, - domains: firewall.domains || [], - is_active: firewall.active ?? true, - edge_functions_enabled: firewall.edgeFunctions ?? false, - network_protection_enabled: firewall.networkProtection ?? false, - waf_enabled: firewall.waf ?? false, - debug_rules: firewall.debugRules ?? false, - }, - }; - - if (firewall.rules && firewall.rules.length > 0) { - payload.rules_engine = firewall.rules.map((rule) => ({ - name: rule.name, - description: rule.description || '', - is_active: rule.active ?? true, - behaviors: this.transformBehaviorsToManifest(rule.behavior), - criteria: rule.criteria - ? [ - rule.criteria.map((criterion) => { - const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; - return { - ...rest, - variable: criterion.variable, - ...(isWithValue && { input_value: inputValue }), - }; - }), - ] - : [ - [ - { - variable: rule.variable, - operator: 'matches', - conditional: 'if', - input_value: rule.match, - }, + return firewalls.map((firewall) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const payload: any = { + main_settings: { + name: firewall.name, + domains: firewall.domains || [], + is_active: firewall.active ?? true, + edge_functions_enabled: firewall.edgeFunctions ?? false, + network_protection_enabled: firewall.networkProtection ?? false, + waf_enabled: firewall.waf ?? false, + debug_rules: firewall.debugRules ?? false, + }, + }; + + if (firewall.rules && firewall.rules.length > 0) { + payload.rules_engine = firewall.rules.map((rule) => ({ + name: rule.name, + description: rule.description || '', + is_active: rule.active ?? true, + behaviors: this.transformBehaviorsToManifest(rule.behavior), + criteria: rule.criteria + ? [ + rule.criteria.map((criterion) => { + const isWithValue = 'inputValue' in criterion; + const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + return { + ...rest, + variable: criterion.variable, + ...(isWithValue && { input_value: inputValue }), + }; + }), + ] + : [ + [ + { + variable: rule.variable, + operator: 'matches', + conditional: 'if', + input_value: rule.match, + }, + ], ], - ], - })); - } + })); + } - return payload; + return payload; + }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -123,45 +129,49 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any transformToConfig(payload: any, transformedPayload: AzionConfig) { const firewall = payload.firewall; - if (!firewall || Object.keys(firewall).length === 0) { + if (!firewall || firewall.length === 0) { return; } - const firewallConfig: AzionFirewall = { - name: firewall.main_settings?.name, - domains: firewall?.main_settings?.domains || [], - active: firewall?.main_settings?.is_active ?? true, - edgeFunctions: firewall?.main_settings?.edge_functions_enabled ?? false, - networkProtection: firewall?.main_settings?.network_protection_enabled ?? false, - waf: firewall?.main_settings?.waf_enabled ?? false, - debugRules: firewall?.main_settings?.debug_rules ?? false, - }; - - if (firewall.rules_engine && firewall.rules_engine.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - firewallConfig.rules = firewall.rules_engine.map((rule: any) => { - const firewallRule: AzionFirewallRule = { - name: rule.name, - description: rule.description || '', - active: rule.is_active ?? true, - behavior: this.transformBehaviorsToConfig(rule.behaviors), - criteria: - // eslint-disable-next-line @typescript-eslint/no-explicit-any - rule.criteria?.[0].map((criterion: any) => { - const isWithValue = 'input_value' in criterion; - const { input_value, ...rest } = criterion; - return { - ...rest, - ...(isWithValue && { inputValue: input_value }), - }; - }) || [], - }; - return firewallRule; - }); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformedPayload.edgeFirewall = firewall.map((firewallPayload: any) => { + const firewallConfig: AzionEdgeFirewall = { + name: firewallPayload.main_settings?.name, + domains: firewallPayload?.main_settings?.domains || [], + active: firewallPayload?.main_settings?.is_active ?? true, + edgeFunctions: firewallPayload?.main_settings?.edge_functions_enabled ?? false, + networkProtection: firewallPayload?.main_settings?.network_protection_enabled ?? false, + waf: firewallPayload?.main_settings?.waf_enabled ?? false, + debugRules: firewallPayload?.main_settings?.debug_rules ?? false, + }; + + if (firewallPayload.rules_engine && firewallPayload.rules_engine.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + firewallConfig.rules = firewallPayload.rules_engine.map((rule: any) => { + const firewallRule: AzionEdgeFirewallRule = { + name: rule.name, + description: rule.description || '', + active: rule.is_active ?? true, + behavior: this.transformBehaviorsToConfig(rule.behaviors), + criteria: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + rule.criteria?.[0].map((criterion: any) => { + const isWithValue = 'input_value' in criterion; + const { input_value, ...rest } = criterion; + return { + ...rest, + ...(isWithValue && { inputValue: input_value }), + }; + }) || [], + }; + return firewallRule; + }); + } + + return firewallConfig; + }); - transformedPayload.firewall = firewallConfig; - return transformedPayload.firewall; + return transformedPayload.edgeFirewall; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts new file mode 100644 index 00000000..849a84b0 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts @@ -0,0 +1,39 @@ +import { AzionConfig } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +/** + * StorageProcessConfigStrategy + * @class StorageProcessConfigStrategy + * @description This class is implementation of the Storage ProcessConfig Strategy. + */ +class StorageProcessConfigStrategy extends ProcessConfigStrategy { + transformToManifest(config: AzionConfig) { + if (!Array.isArray(config?.edgeStorage) || config?.edgeStorage.length === 0) { + return; + } + + return config.edgeStorage.map((item) => ({ + name: item.name, + edge_access: item.edgeAccess || 'read_only', + dir: item.dir, + })); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: any, transformedPayload: AzionConfig) { + const storageConfig = payload.storage; + if (!Array.isArray(storageConfig) || storageConfig.length === 0) { + return; + } + + transformedPayload.edgeStorage = storageConfig.map((item) => ({ + name: item.name, + edgeAccess: item.edge_access || 'read_only', + dir: item.dir, + })); + + return transformedPayload.edgeStorage; + } +} + +export default StorageProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts new file mode 100644 index 00000000..01e5c650 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts @@ -0,0 +1,106 @@ +import { AzionConfig, AzionWorkload } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +class WorkloadProcessConfigStrategy extends ProcessConfigStrategy { + private validateApplicationReferences(config: AzionConfig, workloads: AzionWorkload[]) { + const applicationNames = config.edgeApplications?.map((app) => app.name) || []; + const firewallNames = config.edgeFirewall?.map((firewall) => firewall.name) || []; + + workloads.forEach((workload) => { + if (!applicationNames.includes(workload.edgeApplication)) { + throw new Error( + `Workload "${workload.name}" references non-existent Edge Application "${workload.edgeApplication}".`, + ); + } + }); + + workloads.forEach((workload) => { + if (workload.edgeFirewall && !firewallNames.includes(workload.edgeFirewall)) { + throw new Error( + `Workload "${workload.name}" references non-existent Edge Firewall "${workload.edgeFirewall}".`, + ); + } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any + transformToManifest(config: AzionConfig, context: Record) { + const workloads = config?.workloads; + if (!workloads || workloads.length === 0) return []; + + this.validateApplicationReferences(config, workloads); + + return workloads.map((workload: AzionWorkload) => ({ + name: workload.name, + alternate_domains: workload.alternateDomains || [], + edge_application: workload.edgeApplication, + active: workload.active ?? true, + network_map: workload.networkMap || '1', + edge_firewall: workload.edgeFirewall, + workload_hostname_allow_access: workload.workloadHostnameAllowAccess ?? true, + domains: workload.domains || [], + tls: { + certificate: workload.tls?.certificate || null, + ciphers: workload.tls?.ciphers || null, + minimum_version: workload.tls?.minimumVersion || 'tls_1_2', + }, + protocols: { + http: { + versions: workload.protocols?.http?.versions || ['http1', 'http2'], + http_ports: workload.protocols?.http?.httpPorts || [80], + https_ports: workload.protocols?.http?.httpsPorts || [443], + quic_ports: workload.protocols?.http?.quicPorts || null, + }, + }, + mtls: workload.mtls + ? { + verification: workload.mtls.verification || 'enforce', + certificate: workload.mtls.certificate, + crl: workload.mtls.crl, + } + : undefined, + })); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: { workloads?: any[] }, transformedPayload: AzionConfig) { + if (!payload.workloads || payload.workloads.length === 0) { + return; + } + + transformedPayload.workloads = payload.workloads.map((workload) => ({ + name: workload.name, + alternateDomains: workload.alternate_domains, + edgeApplication: workload.edge_application, + active: workload.active, + networkMap: workload.network_map, + edgeFirewall: workload.edge_firewall, + workloadHostnameAllowAccess: workload.workload_hostname_allow_access, + domains: workload.domains, + tls: { + certificate: workload.tls?.certificate, + ciphers: workload.tls?.ciphers, + minimumVersion: workload.tls?.minimum_version, + }, + protocols: { + http: { + versions: workload.protocols?.http?.versions, + httpPorts: workload.protocols?.http?.http_ports, + httpsPorts: workload.protocols?.http?.https_ports, + quicPorts: workload.protocols?.http?.quic_ports, + }, + }, + mtls: workload.mtls + ? { + verification: workload.mtls.verification, + certificate: workload.mtls.certificate, + crl: workload.mtls.crl, + } + : undefined, + })); + + return transformedPayload.workloads; + } +} + +export default WorkloadProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/index.ts b/packages/config/src/configProcessor/processStrategy/index.ts index b3fc7d93..1bad3ba4 100644 --- a/packages/config/src/configProcessor/processStrategy/index.ts +++ b/packages/config/src/configProcessor/processStrategy/index.ts @@ -1,28 +1,27 @@ -import BuildProcessConfigStrategy from '../processStrategy/implementations/buildProcessConfigStrategy'; -import CacheProcessConfigStrategy from '../processStrategy/implementations/cacheProcessConfigStrategy'; -import DomainProcessConfigStrategy from '../processStrategy/implementations/domainProcessConfigStrategy'; -import OriginProcessConfigStrategy from '../processStrategy/implementations/originProcessConfigStrategy'; -import PurgeProcessConfigStrategy from '../processStrategy/implementations/purgeProcessConfigStrategy'; -import RulesProcessConfigStrategy from '../processStrategy/implementations/rulesProcessConfigStrategy'; -import NetworkListProcessConfigStrategy from '../processStrategy/implementations/secure/networkListProcessConfigStrategy'; -import WafProcessConfigStrategy from '../processStrategy/implementations/secure/wafProcessConfigStrategy'; -import ProcessConfigContext from '../processStrategy/processConfigContext'; +import EdgeApplicationProcessConfigStrategy from './implementations/application/edgeApplicationProcessConfigStrategy'; +import BuildProcessConfigStrategy from './implementations/buildProcessConfigStrategy'; +import EdgeConnectorProcessConfigStrategy from './implementations/edgeConnectorProcessConfigStrategy'; import FunctionsProcessConfigStrategy from './implementations/functionsProcessConfigStrategy'; +import PurgeProcessConfigStrategy from './implementations/purgeProcessConfigStrategy'; import FirewallProcessConfigStrategy from './implementations/secure/firewallProcessConfigStrategy'; +import NetworkListProcessConfigStrategy from './implementations/secure/networkListProcessConfigStrategy'; +import WafProcessConfigStrategy from './implementations/secure/wafProcessConfigStrategy'; +import StorageProcessConfigStrategy from './implementations/storageProcessConfigStrategy'; +import WorkloadProcessConfigStrategy from './implementations/workloadProcessConfigStrategy'; +import ProcessConfigContext from './processConfigContext'; function factoryProcessContext() { const processConfigContext = new ProcessConfigContext(); processConfigContext.setStrategy('build', new BuildProcessConfigStrategy()); - processConfigContext.setStrategy('origin', new OriginProcessConfigStrategy()); - processConfigContext.setStrategy('cache', new CacheProcessConfigStrategy()); - processConfigContext.setStrategy('domain', new DomainProcessConfigStrategy()); processConfigContext.setStrategy('purge', new PurgeProcessConfigStrategy()); processConfigContext.setStrategy('networkList', new NetworkListProcessConfigStrategy()); processConfigContext.setStrategy('waf', new WafProcessConfigStrategy()); + processConfigContext.setStrategy('storage', new StorageProcessConfigStrategy()); processConfigContext.setStrategy('firewall', new FirewallProcessConfigStrategy()); - processConfigContext.setStrategy('functions', new FunctionsProcessConfigStrategy()); - // Rules must be last to apply to behaviors (origin, cache...) - processConfigContext.setStrategy('rules', new RulesProcessConfigStrategy()); + processConfigContext.setStrategy('edgeFunctions', new FunctionsProcessConfigStrategy()); + processConfigContext.setStrategy('edgeApplications', new EdgeApplicationProcessConfigStrategy()); + processConfigContext.setStrategy('workloads', new WorkloadProcessConfigStrategy()); + processConfigContext.setStrategy('edgeConnectors', new EdgeConnectorProcessConfigStrategy()); return processConfigContext; } diff --git a/packages/config/src/configProcessor/processStrategy/processConfigContext.ts b/packages/config/src/configProcessor/processStrategy/processConfigContext.ts index d6543db9..f15c42de 100644 --- a/packages/config/src/configProcessor/processStrategy/processConfigContext.ts +++ b/packages/config/src/configProcessor/processStrategy/processConfigContext.ts @@ -1,4 +1,4 @@ -import { AzionConfig } from '../../types'; +import { AzionConfig } from '../../config/types'; import ProcessConfigStrategy from './processConfigStrategy'; /** diff --git a/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts index d21360ff..2eef7f26 100644 --- a/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts @@ -4,11 +4,11 @@ * @description This class is the base class for all process config strategies. */ -import { AzionConfig } from '../../types'; +import { AzionConfig } from '../../config/types'; class ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToManifest(config: AzionConfig, context: any) { + transformToManifest(config: any, context: any) { return context; } diff --git a/packages/config/src/configProcessor/validateConfig/index.test.ts b/packages/config/src/configProcessor/validateConfig/index.test.ts index b51259ec..814c72e6 100644 --- a/packages/config/src/configProcessor/validateConfig/index.test.ts +++ b/packages/config/src/configProcessor/validateConfig/index.test.ts @@ -8,9 +8,6 @@ describe('generate', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }; expect(() => validateConfig(config)).not.toThrow(); diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts index fdd5c72a..8ff50ee8 100644 --- a/packages/config/src/constants.ts +++ b/packages/config/src/constants.ts @@ -257,9 +257,46 @@ export const RULE_BEHAVIOR_NAMES = [ 'run_function', 'set_cache_policy', 'set_cookie', - 'set_origin', + 'set_edge_connector', ] as const; // Tipos para Rules export type RulePhase = (typeof RULE_PHASES)[number]; export type RuleBehaviorName = (typeof RULE_BEHAVIOR_NAMES)[number]; + +// Workload Constants +export const WORKLOAD_NETWORK_MAP = ['1', '2'] as const; + +export const WORKLOAD_TLS_CIPHERS = ['TLSv1.2_2018', 'TLSv1.2_2019', 'TLSv1.3_2022', 'TLSv1.2_2021'] as const; + +export const WORKLOAD_TLS_VERSIONS = ['', 'tls_1_0', 'tls_1_1', 'tls_1_2', 'tls_1_3'] as const; + +export const WORKLOAD_MTLS_VERIFICATION = ['enforce', 'permissive'] as const; + +export const WORKLOAD_HTTP_VERSIONS = ['http1', 'http2'] as const; + +// Tipos para Workload +export type WorkloadNetworkMap = (typeof WORKLOAD_NETWORK_MAP)[number]; +export type WorkloadTLSCipher = (typeof WORKLOAD_TLS_CIPHERS)[number]; +export type WorkloadTLSVersion = (typeof WORKLOAD_TLS_VERSIONS)[number]; +export type WorkloadMTLSVerification = (typeof WORKLOAD_MTLS_VERIFICATION)[number]; +export type WorkloadHTTPVersion = (typeof WORKLOAD_HTTP_VERSIONS)[number]; + +export const EDGE_CONNECTOR_TYPES = ['http', 's3', 'edge_storage', 'live_ingest'] as const; +export const EDGE_CONNECTOR_LOAD_BALANCE = ['off', 'round_robin', 'ip_hash', 'least_conn'] as const; +export const EDGE_CONNECTOR_SERVER_ROLE = ['primary', 'backup'] as const; +export const EDGE_CONNECTOR_CONNECTION_PREFERENCE = ['IPv4', 'IPv6'] as const; + +export const EDGE_CONNECTOR_REQUIRED_PROPERTIES = { + http: ['versions', 'host', 'path'], + live_ingest: ['endpoint'], + s3: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], + edge_storage: ['bucket'], +} as const; + +export const EDGE_CONNECTOR_ALLOWED_PROPERTIES = { + http: ['versions', 'host', 'path', 'followingRedirect', 'realIpHeader', 'realPortHeader'], + live_ingest: ['endpoint'], + s3: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], + edge_storage: ['bucket', 'prefix'], +} as const; diff --git a/packages/config/src/rules/constants.ts b/packages/config/src/rules/constants.ts new file mode 100644 index 00000000..d4bae5cb --- /dev/null +++ b/packages/config/src/rules/constants.ts @@ -0,0 +1,38 @@ +/** + * Common file extensions used in web applications + */ +export const FILE_EXTENSIONS = { + // Images + IMAGES: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'] as const, + + // Fonts + FONTS: ['ttf', 'otf', 'woff', 'woff2', 'eot'] as const, + + // Documents + DOCUMENTS: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'] as const, + + // Media + MEDIA: ['mp4', 'webm', 'mp3', 'wav', 'ogg'] as const, + + // Code & Data + CODE_AND_DATA: ['css', 'js', 'json', 'xml', 'html', 'txt', 'csv'] as const, + + // Archives + ARCHIVES: ['zip', 'rar', '7z', 'tar', 'gz'] as const, + + // Other + OTHER: ['webmanifest', 'map', 'md', 'yaml', 'yml'] as const, +} as const; + +/** + * All file extensions combined + */ +export const ALL_EXTENSIONS = [ + ...FILE_EXTENSIONS.IMAGES, + ...FILE_EXTENSIONS.FONTS, + ...FILE_EXTENSIONS.DOCUMENTS, + ...FILE_EXTENSIONS.MEDIA, + ...FILE_EXTENSIONS.CODE_AND_DATA, + ...FILE_EXTENSIONS.ARCHIVES, + ...FILE_EXTENSIONS.OTHER, +] as const; diff --git a/packages/config/src/rules/index.ts b/packages/config/src/rules/index.ts new file mode 100644 index 00000000..fc9a5110 --- /dev/null +++ b/packages/config/src/rules/index.ts @@ -0,0 +1,2 @@ +export * from './request/createMPA'; +export * from './request/createSPA'; diff --git a/packages/config/src/rules/request/createMPA.ts b/packages/config/src/rules/request/createMPA.ts new file mode 100644 index 00000000..8e41439b --- /dev/null +++ b/packages/config/src/rules/request/createMPA.ts @@ -0,0 +1,55 @@ +import type { AzionRules } from 'azion/config'; +import { ALL_EXTENSIONS } from '../constants'; + +/** + * Creates rules for a Multi Page Application (MPA) on Azion Edge Platform. + * This configuration is optimized for static site hosting with proper routing and asset delivery. + * + * Features: + * - Static asset delivery with caching + * - Proper handling of directory and subpath routing + * - Automatic index.html handling for clean URLs + * + * @param options Configuration options for the MPA rules + * @param options.bucket The name of the edge storage bucket to use + * @param options.staticExtensions List of file extensions to be treated as static assets + * @returns Array of rules configured for MPA hosting on Azion Edge + */ +export function createMPARules( + options: { + edgeConnector?: string; + staticExtensions?: string[]; + } = {}, +): AzionRules { + const { edgeConnector = 'edge-connector', staticExtensions = ALL_EXTENSIONS } = options; + + return { + request: [ + { + name: 'Deliver Static Assets', + match: `\\.(${staticExtensions.join('|')})$`, + behavior: { + setEdgeConnector: edgeConnector, + deliver: true, + }, + }, + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + setEdgeConnector: edgeConnector, + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + setEdgeConnector: edgeConnector, + rewrite: '${uri}/index.html', + }, + }, + ], + response: [], + }; +} diff --git a/packages/config/src/rules/request/createSPA.ts b/packages/config/src/rules/request/createSPA.ts new file mode 100644 index 00000000..f10d97f2 --- /dev/null +++ b/packages/config/src/rules/request/createSPA.ts @@ -0,0 +1,47 @@ +import type { AzionRules } from 'azion/config'; +import { ALL_EXTENSIONS } from '../constants'; + +/** + * Creates rules for a Single Page Application (SPA) on Azion Edge Platform. + * This configuration is optimized for SPA hosting with proper routing and asset delivery. + * + * Features: + * - Static asset delivery with caching + * - Client-side routing support + * - Automatic fallback to index.html for all routes + * + * @param options Configuration options for the SPA rules + * @param options.bucket The name of the edge storage bucket to use + * @param options.staticExtensions List of file extensions to be treated as static assets + * @returns Array of rules configured for SPA hosting on Azion Edge + */ +export function createSPARules( + options: { + edgeConnector?: string; + staticExtensions?: string[]; + } = {}, +): AzionRules { + const { edgeConnector = 'edge-connector', staticExtensions = ALL_EXTENSIONS } = options; + + return { + request: [ + { + name: 'Deliver Static Assets', + match: `\\.(${staticExtensions.join('|')})$`, + behavior: { + setEdgeConnector: edgeConnector, + deliver: true, + }, + }, + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + setEdgeConnector: edgeConnector, + rewrite: '/index.html', + }, + }, + ], + response: [], + }; +} diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9095200c..dde7cd81 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -1,4 +1,7 @@ import { + EDGE_CONNECTOR_CONNECTION_PREFERENCE, + EDGE_CONNECTOR_LOAD_BALANCE, + EDGE_CONNECTOR_TYPES, FirewallRateLimitBy, FirewallRateLimitType, FirewallWafMode, @@ -9,6 +12,11 @@ import { RuleVariable, WafMode, WafSensitivity, + WorkloadHTTPVersion, + WorkloadMTLSVerification, + WorkloadNetworkMap, + WorkloadTLSCipher, + WorkloadTLSVersion, } from './constants'; import { FetchEvent } from 'azion/types'; @@ -47,56 +55,6 @@ export type AzionDomain = { }; }; -/** - * Origin configuration for Azion. - */ -export type AzionOrigin = { - /** Origin ID */ - id?: number; - /** Origin key */ - key?: string; - /** Origin name */ - name: string; - /** Origin type */ - type: string; - /** Bucket name for S3-like origins */ - bucket?: string | null; - /** Prefix for S3-like origins */ - prefix?: string | null; - /** Addresses for the origin */ - addresses?: - | string[] - | { - /** Address of the origin */ - address: string; - /** Weight for load balancing */ - weight?: number; - }[]; - /** Host header to be sent to the origin */ - hostHeader?: string; - /** Protocol policy for communicating with the origin */ - protocolPolicy?: 'http' | 'https' | 'preserve'; - /** Indicates if redirection should be used */ - redirection?: boolean; - /** Load balancing method */ - method?: 'ip_hash' | 'least_connections' | 'round_robin'; - /** Path to be appended to the origin address */ - path?: string; - /** Connection timeout in seconds */ - connectionTimeout?: number; - /** Timeout between bytes in seconds */ - timeoutBetweenBytes?: number; - /** HMAC authentication configuration */ - hmac?: { - /** AWS region */ - region: string; - /** AWS access key */ - accessKey: string; - /** AWS secret key */ - secretKey: string; - }; -}; - /** * Cache configuration for Azion. */ @@ -179,17 +137,8 @@ export type AzionRequestRule = { criteria?: AzionRuleCriteria[]; /** Behavior to be applied when the rule matches */ behavior?: { - /** Set a new origin */ - setOrigin?: { - /** Origin name */ - name: string; - /** Origin type */ - type: string; - }; /** Rewrite the request */ rewrite?: string; - /** Set headers */ - setHeaders?: string[]; /** Bypass cache */ bypassCache?: boolean | null; /** Force HTTPS */ @@ -238,6 +187,16 @@ export type AzionRequestRule = { /** CDN cache TTL */ cdn_cache_settings_maximum_ttl?: number | null; }; + /** Finish request phase */ + finishRequestPhase?: boolean; + /** Set Edge connector */ + setEdgeConnector?: string; + /** Add request header */ + addRequestHeader?: string[]; + /** Add request cookie */ + addRequestCookie?: string; + /** Filter request cookie */ + filterRequestCookie?: string; }; }; @@ -259,8 +218,6 @@ export type AzionResponseRule = { criteria?: AzionRuleCriteria[]; /** Behavior to be applied when the rule matches */ behavior?: { - /** Set a cookie */ - setCookie?: string | null; /** Set headers */ setHeaders?: string[]; /** Deliver the content */ @@ -276,8 +233,6 @@ export type AzionResponseRule = { }; /** Enable GZIP compression */ enableGZIP?: boolean | null; - /** Filter a cookie */ - filterCookie?: string | null; /** Filter a header */ filterHeader?: string | null; /** Run a serverless function */ @@ -286,6 +241,10 @@ export type AzionResponseRule = { redirectTo301?: string | null; /** Redirect with 302 status */ redirectTo302?: string | null; + /** Add response header */ + addResponseHeader?: string[]; + /** Filter response cookie */ + filterResponseCookie?: string; }; }; @@ -341,48 +300,105 @@ export type AzionNetworkList = { listContent: string[] | number[]; }; +/** + * Storage binding configuration for Azion. + */ +export type AzionStorageBinding = { + /** Storage bucket name */ + bucket: string; + /** Storage prefix */ + prefix: string; +}; + +/** + * Bindings configuration for Azion. + */ +export type AzionBindings = { + /** Storage bindings */ + storage?: AzionStorageBinding; +}; + /** * Function configuration for Azion. */ -export type AzionFunction = { +export type AzionEdgeFunction = { /** Function name */ name: string; /** Function path */ path: string; /** Optional arguments to be passed to the function */ args?: Record; + /** Function bindings */ + bindings?: AzionBindings; +}; + +/** + * Storage configuration for Azion. + */ +export type AzionBucket = { + /** Storage name */ + name: string; + /** Edge access type */ + edgeAccess?: 'read_only' | 'read_write' | 'restricted'; + /** Storage path */ + dir: string; }; +/** + * Edge Application configuration for Azion. + */ +export type AzionEdgeApplication = { + /** Application name */ + name: string; + /** Enable edge cache */ + edgeCacheEnabled?: boolean; + /** Enable edge functions */ + edgeFunctionsEnabled?: boolean; + /** Enable application accelerator */ + applicationAcceleratorEnabled?: boolean; + /** Enable image processor */ + imageProcessorEnabled?: boolean; + /** Enable tiered cache */ + tieredCacheEnabled?: boolean; + /** Indicates if the application is active */ + active?: boolean; + /** Enable debug mode */ + debug?: boolean; + /** Cache settings */ + cache?: AzionCache[]; + /** Rules configuration */ + rules?: AzionRules; +}; /** * Main configuration type for Azion. */ export type AzionConfig = { /** Build configuration */ build?: AzionBuild; - /** Domain configuration */ - domain?: AzionDomain; - /** Origin configurations */ - origin?: AzionOrigin[]; - /** Cache configurations */ - cache?: AzionCache[]; + /** Edge Application configuration */ + edgeApplications?: AzionEdgeApplication[]; /** Functions configurations */ - functions?: AzionFunction[]; - /** Rules configuration */ - rules?: AzionRules; - /** Purge configurations */ - purge?: AzionPurge[]; + edgeFunctions?: AzionEdgeFunction[]; + /** Edge Connectors configuration */ + edgeConnectors?: AzionEdgeConnector[]; + /** Storage configurations */ + edgeStorage?: AzionBucket[]; /** Firewall configuration */ - firewall?: AzionFirewall; + edgeFirewall?: AzionEdgeFirewall[]; /** Network list configurations */ networkList?: AzionNetworkList[]; + /** Purge configurations */ + purge?: AzionPurge[]; /** WAF configuration */ waf?: AzionWaf[]; + /** Workload configuration */ + workloads?: AzionWorkload[]; }; /** * Firewall behavior configuration for Azion. */ -export type AzionFirewallBehavior = { +export type AzionEdgeFirewallBehavior = { /** Run a serverless function */ runFunction?: string; /** Set WAF ruleset */ @@ -418,31 +434,31 @@ export type AzionFirewallBehavior = { }; }; -export type AzionFirewallCriteriaBase = { +export type AzionEdgeFirewallCriteriaBase = { /** Variable to be evaluated */ variable: RuleVariable; /** Conditional type */ conditional: RuleConditional; }; -export type AzionFirewallCriteriaWithValue = AzionFirewallCriteriaBase & { +export type AzionEdgeFirewallCriteriaWithValue = AzionEdgeFirewallCriteriaBase & { /** Operator for comparison that requires input value */ operator: RuleOperatorWithValue; /** Input value for comparison */ inputValue: string; }; -export type AzionFirewallCriteriaWithoutValue = AzionFirewallCriteriaBase & { +export type AzionEdgeFirewallCriteriaWithoutValue = AzionEdgeFirewallCriteriaBase & { /** Operator for comparison that doesn't require input value */ operator: RuleOperatorWithoutValue; }; -export type AzionFirewallCriteria = AzionFirewallCriteriaWithValue | AzionFirewallCriteriaWithoutValue; +export type AzionEdgeFirewallCriteria = AzionEdgeFirewallCriteriaWithValue | AzionEdgeFirewallCriteriaWithoutValue; /** * Firewall rule configuration for Azion. */ -export type AzionFirewallRule = { +export type AzionEdgeFirewallRule = { /** Rule name */ name: string; /** Rule description */ @@ -454,15 +470,15 @@ export type AzionFirewallRule = { /** Variable to be used in the match */ variable?: RuleVariable; /** Array of criteria for complex conditions */ - criteria?: AzionFirewallCriteria[]; + criteria?: AzionEdgeFirewallCriteria[]; /** Behavior to be applied when the rule matches */ - behavior: AzionFirewallBehavior; + behavior: AzionEdgeFirewallBehavior; }; /** * Firewall configuration for Azion. */ -export type AzionFirewall = { +export type AzionEdgeFirewall = { /** Firewall name */ name: string; /** List of domains */ @@ -478,7 +494,7 @@ export type AzionFirewall = { /** Variable to be used in the match */ variable?: RuleVariable; /** List of firewall rules */ - rules?: AzionFirewallRule[]; + rules?: AzionEdgeFirewallRule[]; /** Debug mode */ debugRules?: boolean; }; @@ -588,3 +604,114 @@ export interface AzionPrebuildResult { export interface AzionConfigs { configs: AzionConfig[]; } + +export type AzionWorkloadTLS = { + certificate?: number | null; + ciphers?: WorkloadTLSCipher | null; + minimumVersion?: WorkloadTLSVersion | null; +}; + +export type AzionWorkloadProtocols = { + http: { + versions: WorkloadHTTPVersion[]; + httpPorts: number[]; + httpsPorts: number[]; + quicPorts?: number[] | null; + }; +}; + +export type AzionWorkloadMTLS = { + verification: WorkloadMTLSVerification; + certificate?: number | null; + crl?: number[] | null; +}; + +export type AzionWorkload = { + name: string; + alternateDomains?: string[]; + edgeApplication: string; + active?: boolean; + networkMap?: WorkloadNetworkMap; + edgeFirewall?: string | null; + tls?: AzionWorkloadTLS; + protocols?: AzionWorkloadProtocols; + mtls?: AzionWorkloadMTLS; + domains: string[]; + workloadHostnameAllowAccess?: boolean; +}; + +export type EdgeConnectorType = (typeof EDGE_CONNECTOR_TYPES)[number]; +export type EdgeConnectorLoadBalance = (typeof EDGE_CONNECTOR_LOAD_BALANCE)[number]; +export type EdgeConnectorConnectionPreference = (typeof EDGE_CONNECTOR_CONNECTION_PREFERENCE)[number]; + +export interface EdgeConnectorModules { + loadBalancerEnabled: boolean; + originShieldEnabled: boolean; +} + +export interface EdgeConnectorTLS { + policy: string; +} + +export interface EdgeConnectorAddress { + address: string; + plainPort?: number; + tlsPort?: number; + serverRole?: 'primary' | 'backup'; + weight?: number; + active?: boolean; + maxConns?: number; + maxFails?: number; + failTimeout?: number; + tls?: { + policy: 'off' | 'on' | 'preserve'; + }; +} + +export interface HttpTypeProperty { + versions: string[]; + host: string; + path: string; + followingRedirect?: boolean; + realIpHeader?: string; + realPortHeader?: string; +} + +export interface LiveIngestTypeProperty { + endpoint: string; +} + +export interface S3TypeProperty { + host: string; + bucket: string; + path: string; + region: string; + accessKey: string; + secretKey: string; +} + +export interface StorageTypeProperty { + bucket: string; + prefix?: string; +} + +export type EdgeConnectorTypeProperty = + | HttpTypeProperty + | LiveIngestTypeProperty + | S3TypeProperty + | StorageTypeProperty; + +export interface AzionEdgeConnector { + name: string; + modules: EdgeConnectorModules; + active?: boolean; + type: EdgeConnectorType; + typeProperties: EdgeConnectorTypeProperty; + addresses?: EdgeConnectorAddress[]; + tls?: EdgeConnectorTLS; + loadBalanceMethod?: EdgeConnectorLoadBalance; + connectionPreference?: EdgeConnectorConnectionPreference[]; + connectionTimeout?: number; + readWriteTimeout?: number; + maxRetries?: number; +} diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json index 18fe1ee3..12a498a3 100644 --- a/packages/config/tsconfig.json +++ b/packages/config/tsconfig.json @@ -13,7 +13,8 @@ "baseUrl": ".", "paths": { "azion/types": ["../types/src/index.ts"], - "azion/bundler": ["../bundler/src/index.ts"] + "azion/bundler": ["../bundler/src/index.ts"], + "azion/config": ["../config/src/index.ts"] } } } diff --git a/packages/config/tsup.config.ts b/packages/config/tsup.config.ts new file mode 100644 index 00000000..c84b69e2 --- /dev/null +++ b/packages/config/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts', 'src/rules/index.ts'], + format: ['cjs', 'esm'], + splitting: true, + sourcemap: false, + clean: true, + bundle: true, + dts: true, + minify: true, + minifyWhitespace: true, +}); diff --git a/packages/presets/src/presets/angular/config.ts b/packages/presets/src/presets/angular/config.ts index 01cf1026..bf92514a 100644 --- a/packages/presets/src/presets/angular/config.ts +++ b/packages/presets/src/presets/angular/config.ts @@ -1,50 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createSPARules } from 'azion/config/rules'; -const config = defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'angular', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createSPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; export default config; diff --git a/packages/presets/src/presets/angular/index.ts b/packages/presets/src/presets/angular/index.ts index 6ad3d670..d6caaf1f 100644 --- a/packages/presets/src/presets/angular/index.ts +++ b/packages/presets/src/presets/angular/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const angular: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Angular: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/astro/config.ts b/packages/presets/src/presets/astro/config.ts index f4c5244e..873fec70 100644 --- a/packages/presets/src/presets/astro/config.ts +++ b/packages/presets/src/presets/astro/config.ts @@ -1,56 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -const config = defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'astro', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; export default config; diff --git a/packages/presets/src/presets/astro/index.ts b/packages/presets/src/presets/astro/index.ts index c7f67c3e..3c4d7ee0 100644 --- a/packages/presets/src/presets/astro/index.ts +++ b/packages/presets/src/presets/astro/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const astro: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Astro: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/docusaurus/config.ts b/packages/presets/src/presets/docusaurus/config.ts index 03af6e00..873fec70 100644 --- a/packages/presets/src/presets/docusaurus/config.ts +++ b/packages/presets/src/presets/docusaurus/config.ts @@ -1,56 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -const config = defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'docusaurus', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; export default config; diff --git a/packages/presets/src/presets/docusaurus/index.ts b/packages/presets/src/presets/docusaurus/index.ts index 4e343548..766f7c56 100644 --- a/packages/presets/src/presets/docusaurus/index.ts +++ b/packages/presets/src/presets/docusaurus/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const docusaurus: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Docusaurus: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/eleventy/config.ts b/packages/presets/src/presets/eleventy/config.ts index 6af8bd85..873fec70 100644 --- a/packages/presets/src/presets/eleventy/config.ts +++ b/packages/presets/src/presets/eleventy/config.ts @@ -1,56 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -const config = defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'eleventy', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; export default config; diff --git a/packages/presets/src/presets/eleventy/index.ts b/packages/presets/src/presets/eleventy/index.ts index c7cae4ed..695e74e3 100644 --- a/packages/presets/src/presets/eleventy/index.ts +++ b/packages/presets/src/presets/eleventy/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const eleventy: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Eleventy: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/emscripten/config.ts b/packages/presets/src/presets/emscripten/config.ts index 370859b9..592799b4 100644 --- a/packages/presets/src/presets/emscripten/config.ts +++ b/packages/presets/src/presets/emscripten/config.ts @@ -1,10 +1,10 @@ -import { AzionBuild, defineConfig } from 'azion/config'; +import type { AzionBuild, AzionConfig } from 'azion/config'; import webpack, { Configuration } from 'webpack'; -const config = defineConfig({ +const config: AzionConfig = { build: { - entry: 'handler.ts', - bundler: 'esbuild', + entry: 'handler.js', + bundler: 'webpack', polyfills: false, extend: (context: Configuration) => { context = { @@ -33,23 +33,28 @@ const config = defineConfig({ return context; }, } as AzionBuild, - functions: [ + edgeFunctions: [ { - name: 'my-emscripten-function', - path: '.edge/functions/handler.js', + name: '$EDGE_FUNCTION_NAME', + path: '$LOCAL_FUNCTION_PATH', }, ], - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: 'my-emscripten-function', - }, + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: '$EDGE_FUNCTION_NAME', + }, + }, + ], }, - ], - }, -}); + }, + ], +}; export default config; diff --git a/packages/presets/src/presets/emscripten/index.ts b/packages/presets/src/presets/emscripten/index.ts index d90fcee4..e9ee6bdf 100644 --- a/packages/presets/src/presets/emscripten/index.ts +++ b/packages/presets/src/presets/emscripten/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const emscripten: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Emscripten: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/gatsby/config.ts b/packages/presets/src/presets/gatsby/config.ts index 25997f6d..873fec70 100644 --- a/packages/presets/src/presets/gatsby/config.ts +++ b/packages/presets/src/presets/gatsby/config.ts @@ -1,54 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'gatsby', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/gatsby/index.ts b/packages/presets/src/presets/gatsby/index.ts index b9664b16..efcb19ad 100644 --- a/packages/presets/src/presets/gatsby/index.ts +++ b/packages/presets/src/presets/gatsby/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const gatsby: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Gatsby: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/hexo/config.ts b/packages/presets/src/presets/hexo/config.ts index 02311de9..873fec70 100644 --- a/packages/presets/src/presets/hexo/config.ts +++ b/packages/presets/src/presets/hexo/config.ts @@ -1,54 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'hexo', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/hexo/index.ts b/packages/presets/src/presets/hexo/index.ts index c904fdea..4f9aab3c 100644 --- a/packages/presets/src/presets/hexo/index.ts +++ b/packages/presets/src/presets/hexo/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const hexo: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Hexo: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/html/config.ts b/packages/presets/src/presets/html/config.ts index 00f79162..873fec70 100644 --- a/packages/presets/src/presets/html/config.ts +++ b/packages/presets/src/presets/html/config.ts @@ -1,29 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'html', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - ], - }, -}); + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', + }, + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/html/index.ts b/packages/presets/src/presets/html/index.ts index 5a68e0ed..2ed87091 100644 --- a/packages/presets/src/presets/html/index.ts +++ b/packages/presets/src/presets/html/index.ts @@ -3,4 +3,4 @@ import config from './config'; import handler from './handler'; import metadata from './metadata'; -export const html: AzionBuildPreset = { config, metadata, handler }; +export const HTML: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/hugo/config.ts b/packages/presets/src/presets/hugo/config.ts index cb463046..873fec70 100644 --- a/packages/presets/src/presets/hugo/config.ts +++ b/packages/presets/src/presets/hugo/config.ts @@ -1,54 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'hugo', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/hugo/index.ts b/packages/presets/src/presets/hugo/index.ts index e145e93a..64c10847 100644 --- a/packages/presets/src/presets/hugo/index.ts +++ b/packages/presets/src/presets/hugo/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const hugo: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Hugo: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/javascript/config.ts b/packages/presets/src/presets/javascript/config.ts index 23288501..def01da7 100644 --- a/packages/presets/src/presets/javascript/config.ts +++ b/packages/presets/src/presets/javascript/config.ts @@ -1,26 +1,30 @@ -import { defineConfig } from 'azion/config'; - -export default defineConfig({ +import type { AzionConfig } from 'azion/config'; +const config: AzionConfig = { build: { - entry: 'handler.ts', - preset: 'javascript', - polyfills: true, + entry: 'handler.js', }, - functions: [ + edgeFunctions: [ { - name: 'my-javascript-function', - path: '.edge/functions/handler.js', + name: '$EDGE_FUNCTION_NAME', + path: '$LOCAL_FUNCTION_PATH', }, ], - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: 'my-javascript-function', - }, + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: '$EDGE_FUNCTION_NAME', + }, + }, + ], }, - ], - }, -}); + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/javascript/index.ts b/packages/presets/src/presets/javascript/index.ts index 7397d535..a030b2fa 100644 --- a/packages/presets/src/presets/javascript/index.ts +++ b/packages/presets/src/presets/javascript/index.ts @@ -2,4 +2,4 @@ import type { AzionBuildPreset } from 'azion/config'; import config from './config'; import metadata from './metadata'; -export const javascript: AzionBuildPreset = { config, metadata }; +export const JavaScript: AzionBuildPreset = { config, metadata }; diff --git a/packages/presets/src/presets/jekyll/config.ts b/packages/presets/src/presets/jekyll/config.ts index 76bdbe91..873fec70 100644 --- a/packages/presets/src/presets/jekyll/config.ts +++ b/packages/presets/src/presets/jekyll/config.ts @@ -1,54 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'jekyll', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/jekyll/index.ts b/packages/presets/src/presets/jekyll/index.ts index bf2ac691..30c9c362 100644 --- a/packages/presets/src/presets/jekyll/index.ts +++ b/packages/presets/src/presets/jekyll/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const jekyll: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Jekyll: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/next/config.ts b/packages/presets/src/presets/next/config.ts index 4b63f86b..390f9199 100644 --- a/packages/presets/src/presets/next/config.ts +++ b/packages/presets/src/presets/next/config.ts @@ -1,55 +1,68 @@ -import { defineConfig } from 'azion/config'; - -export default defineConfig({ +import type { AzionConfig } from 'azion/config'; +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'next', polyfills: true, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - functions: [ + edgeConnectors: [ { - name: 'handler', - path: '.edge/functions/handler.js', + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, + }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', + }, }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/_next\\/static\\/', // starts with '/_next/static/' - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', + edgeFunctions: [ + { + name: '$EDGE_FUNCTION_NAME', + path: '$LOCAL_FUNCTION_PATH', + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: { + request: [ + { + name: 'Next.js Static Assets', + match: '^\\/_next\\/static\\/', + behavior: { + setEdgeConnector: '$EDGE_CONNECTOR_NAME', + deliver: true, + }, }, - deliver: true, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setEdgeConnector: '$EDGE_CONNECTOR_NAME', + deliver: true, + }, }, - deliver: true, - }, - }, - { - name: 'Execute Edge Function', - match: '^/', - behavior: { - runFunction: 'handler', - forwardCookies: true, - }, + { + name: 'Execute Next.js Function', + match: '^\\/', + behavior: { + runFunction: '$EDGE_FUNCTION_NAME', + forwardCookies: true, + }, + }, + ], }, - ], - }, -}); + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/next/index.ts b/packages/presets/src/presets/next/index.ts index 04b642d3..2f4b2c97 100644 --- a/packages/presets/src/presets/next/index.ts +++ b/packages/presets/src/presets/next/index.ts @@ -6,4 +6,4 @@ import postbuild from './postbuild'; import prebuild from './prebuild'; ``; -export const next: AzionBuildPreset = { config, metadata, handler, prebuild, postbuild }; +export const Next: AzionBuildPreset = { config, metadata, handler, prebuild, postbuild }; diff --git a/packages/presets/src/presets/nuxt/config.ts b/packages/presets/src/presets/nuxt/config.ts index bb01b0d9..873fec70 100644 --- a/packages/presets/src/presets/nuxt/config.ts +++ b/packages/presets/src/presets/nuxt/config.ts @@ -1,56 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'nuxt', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - // eslint-disable-next-line no-template-curly-in-string - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - // eslint-disable-next-line no-template-curly-in-string - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/nuxt/index.ts b/packages/presets/src/presets/nuxt/index.ts index 9757eb93..70607442 100644 --- a/packages/presets/src/presets/nuxt/index.ts +++ b/packages/presets/src/presets/nuxt/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const nuxt: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Nuxt: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/preact/config.ts b/packages/presets/src/presets/preact/config.ts index 5b9552f7..873fec70 100644 --- a/packages/presets/src/presets/preact/config.ts +++ b/packages/presets/src/presets/preact/config.ts @@ -1,46 +1,38 @@ -import { defineConfig } from 'azion/config'; -export default defineConfig({ +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; + +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'preact', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/preact/index.ts b/packages/presets/src/presets/preact/index.ts index 6766e028..da7b74c4 100644 --- a/packages/presets/src/presets/preact/index.ts +++ b/packages/presets/src/presets/preact/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; // import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const preact: AzionBuildPreset = { config, metadata, handler }; +export const Preact: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/qwik/config.ts b/packages/presets/src/presets/qwik/config.ts index fa38186f..873fec70 100644 --- a/packages/presets/src/presets/qwik/config.ts +++ b/packages/presets/src/presets/qwik/config.ts @@ -1,47 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'qwik', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/qwik/index.ts b/packages/presets/src/presets/qwik/index.ts index 85a666e8..194b555e 100644 --- a/packages/presets/src/presets/qwik/index.ts +++ b/packages/presets/src/presets/qwik/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const qwik: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Qwik: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/react/config.ts b/packages/presets/src/presets/react/config.ts index 1230a6d0..bf92514a 100644 --- a/packages/presets/src/presets/react/config.ts +++ b/packages/presets/src/presets/react/config.ts @@ -1,49 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createSPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'react', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createSPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, - }, - ], - }, -}); +export default config; diff --git a/packages/presets/src/presets/react/index.ts b/packages/presets/src/presets/react/index.ts index ee9cfa51..5e345b13 100644 --- a/packages/presets/src/presets/react/index.ts +++ b/packages/presets/src/presets/react/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const react: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const React: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/rustwasm/config.ts b/packages/presets/src/presets/rustwasm/config.ts index 47678870..d354c9d5 100644 --- a/packages/presets/src/presets/rustwasm/config.ts +++ b/packages/presets/src/presets/rustwasm/config.ts @@ -1,11 +1,11 @@ -import { AzionBuild, defineConfig } from 'azion/config'; +import type { AzionBuild, AzionConfig } from 'azion/config'; import webpack, { Configuration } from 'webpack'; -export default defineConfig({ +const config: AzionConfig = { build: { - entry: 'handler.ts', - preset: 'rustwasm', + entry: 'handler.js', polyfills: false, + bundler: 'webpack', extend: (context: Configuration) => { context = { ...context, @@ -33,21 +33,28 @@ export default defineConfig({ return context; }, } as AzionBuild, - functions: [ + edgeFunctions: [ { - name: 'my-rustwasm-function', - path: '.edge/functions/handler.js', + name: '$EDGE_FUNCTION_NAME', + path: '$LOCAL_FUNCTION_PATH', }, ], - rules: { - request: [ - { - name: 'Execute Edge F nction', - match: '^\\/', - behavior: { - runFunction: 'my-rustwasm-function', - }, + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: '$EDGE_FUNCTION_NAME', + }, + }, + ], }, - ], - }, -}); + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/rustwasm/index.ts b/packages/presets/src/presets/rustwasm/index.ts index f6836ad3..4fc8968b 100644 --- a/packages/presets/src/presets/rustwasm/index.ts +++ b/packages/presets/src/presets/rustwasm/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; // import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const rustwasm: AzionBuildPreset = { config, metadata, handler }; +export const RustWASM: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/stencil/config.ts b/packages/presets/src/presets/stencil/config.ts index 9835a9d4..bf92514a 100644 --- a/packages/presets/src/presets/stencil/config.ts +++ b/packages/presets/src/presets/stencil/config.ts @@ -1,47 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createSPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { - preset: 'stencil', + bundler: 'esbuild', }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createSPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, - }, - ], - }, -}); +export default config; diff --git a/packages/presets/src/presets/stencil/index.ts b/packages/presets/src/presets/stencil/index.ts index dce41512..41e351fc 100644 --- a/packages/presets/src/presets/stencil/index.ts +++ b/packages/presets/src/presets/stencil/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const stencil: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Stencil: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/svelte/config.ts b/packages/presets/src/presets/svelte/config.ts index 795b8c08..873fec70 100644 --- a/packages/presets/src/presets/svelte/config.ts +++ b/packages/presets/src/presets/svelte/config.ts @@ -1,56 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'svelte', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - // eslint-disable-next-line no-template-curly-in-string - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - // eslint-disable-next-line no-template-curly-in-string - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/svelte/index.ts b/packages/presets/src/presets/svelte/index.ts index f367ac19..6e693f49 100644 --- a/packages/presets/src/presets/svelte/index.ts +++ b/packages/presets/src/presets/svelte/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const svelte: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const Svelte: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/typescript/config.ts b/packages/presets/src/presets/typescript/config.ts index 675f425e..686cff8c 100644 --- a/packages/presets/src/presets/typescript/config.ts +++ b/packages/presets/src/presets/typescript/config.ts @@ -1,26 +1,30 @@ -import { defineConfig } from 'azion/config'; - -export default defineConfig({ +import type { AzionConfig } from 'azion/config'; +const config: AzionConfig = { build: { entry: 'handler.ts', - preset: 'typescript', - polyfills: true, }, - functions: [ + edgeFunctions: [ { - name: 'my-typescript-function', - path: '.edge/functions/handler.js', + name: '$EDGE_FUNCTION_NAME', + path: '$LOCAL_FUNCTION_PATH', }, ], - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: 'my-typescript-function', - }, + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: '$EDGE_FUNCTION_NAME', + }, + }, + ], }, - ], - }, -}); + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/typescript/index.ts b/packages/presets/src/presets/typescript/index.ts index f482a3c7..b5e8cd97 100644 --- a/packages/presets/src/presets/typescript/index.ts +++ b/packages/presets/src/presets/typescript/index.ts @@ -2,4 +2,4 @@ import type { AzionBuildPreset } from 'azion/config'; import config from './config'; import metadata from './metadata'; -export const typescript: AzionBuildPreset = { config, metadata }; +export const TypeScript: AzionBuildPreset = { config, metadata }; diff --git a/packages/presets/src/presets/vitepress/config.ts b/packages/presets/src/presets/vitepress/config.ts index c23405b3..873fec70 100644 --- a/packages/presets/src/presets/vitepress/config.ts +++ b/packages/presets/src/presets/vitepress/config.ts @@ -1,54 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'vitepress', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, - }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - rewrite: '${uri}index.html', - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - rewrite: '${uri}/index.html', - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, - ], - }, -}); + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; + +export default config; diff --git a/packages/presets/src/presets/vitepress/index.ts b/packages/presets/src/presets/vitepress/index.ts index c4430f21..64193d54 100644 --- a/packages/presets/src/presets/vitepress/index.ts +++ b/packages/presets/src/presets/vitepress/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const vitepress: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const VitePress: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/vue/config.ts b/packages/presets/src/presets/vue/config.ts index 22ef8bd8..bf92514a 100644 --- a/packages/presets/src/presets/vue/config.ts +++ b/packages/presets/src/presets/vue/config.ts @@ -1,48 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createSPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { - preset: 'vue', bundler: 'esbuild', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createSPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, - }, - ], - }, -}); +export default config; diff --git a/packages/presets/src/presets/vue/index.ts b/packages/presets/src/presets/vue/index.ts index 63bc0f7c..5db21c24 100644 --- a/packages/presets/src/presets/vue/index.ts +++ b/packages/presets/src/presets/vue/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const vue: AzionBuildPreset = { config, handler, metadata, prebuild }; +export const Vue: AzionBuildPreset = { config, handler, metadata, prebuild }; diff --git a/packages/presets/src/presets/vuepress/config.ts b/packages/presets/src/presets/vuepress/config.ts index 33e710af..873fec70 100644 --- a/packages/presets/src/presets/vuepress/config.ts +++ b/packages/presets/src/presets/vuepress/config.ts @@ -1,48 +1,38 @@ -import { defineConfig } from 'azion/config'; +import type { AzionConfig } from 'azion/config'; +import { createMPARules } from 'azion/config/rules'; -export default defineConfig({ +const config: AzionConfig = { build: { bundler: 'esbuild', - preset: 'vuepress', - polyfills: false, }, - origin: [ + edgeStorage: [ { - name: 'origin-storage-default', - type: 'object_storage', + name: '$BUCKET_NAME', + dir: '$LOCAL_BUCKET_DIR', + edgeAccess: 'read_only', }, ], - rules: { - request: [ - { - name: 'Set Storage Origin for All Requests', - match: '^\\/', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - }, + edgeConnectors: [ + { + name: '$EDGE_CONNECTOR_NAME', + modules: { + loadBalancerEnabled: false, + originShieldEnabled: false, }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setOrigin: { - name: 'origin-storage-default', - type: 'object_storage', - }, - deliver: true, - }, + type: 'edge_storage', + typeProperties: { + bucket: '$BUCKET_NAME', }, + }, + ], + edgeApplications: [ + { + name: '$EDGE_APPLICATION_NAME', + rules: createMPARules({ + edgeConnector: '$EDGE_CONNECTOR_NAME', + }), + }, + ], +}; - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - rewrite: `/index.html`, - }, - }, - ], - }, -}); +export default config; diff --git a/packages/presets/src/presets/vuepress/index.ts b/packages/presets/src/presets/vuepress/index.ts index 4df290a4..e3380694 100644 --- a/packages/presets/src/presets/vuepress/index.ts +++ b/packages/presets/src/presets/vuepress/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const vuepress: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const VuePress: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/tsconfig.json b/packages/presets/tsconfig.json index 0c85efa5..01933ba5 100644 --- a/packages/presets/tsconfig.json +++ b/packages/presets/tsconfig.json @@ -16,6 +16,7 @@ "azion/utils/edge": ["../utils/src/edge/index.ts"], "azion/utils/node": ["../utils/src/node/index.ts"], "azion/config": ["../config/src/index.ts"], + "azion/config/rules": ["../config/src/rules/index.ts"], "azion/presets": ["../presets/src/index.ts"], "azion/bundler": ["../bundler/src/index.ts"], "azion/types": ["../types/src/index.ts"] From 9c01de4bc2beba5536f4b77b60948bee51e35197 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 27 May 2025 21:06:38 +0000 Subject: [PATCH 08/34] chore(release): 1.20.0-stage.4 [skip ci] ## [1.20.0-stage.4](https://github.com/aziontech/lib/compare/v1.20.0-stage.3...v1.20.0-stage.4) (2025-05-27) ### Features * **config:** azion api v4 (#161) ([49c3f02](https://github.com/aziontech/lib/commit/49c3f02aa39d5e24eb2141db2370a26e63f8fe10)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f225581a..40828a74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.4](https://github.com/aziontech/lib/compare/v1.20.0-stage.3...v1.20.0-stage.4) (2025-05-27) + + +### Features + +* **config:** azion api v4 (#161) ([49c3f02](https://github.com/aziontech/lib/commit/49c3f02aa39d5e24eb2141db2370a26e63f8fe10)) + ## [1.20.0-stage.3](https://github.com/aziontech/lib/compare/v1.20.0-stage.2...v1.20.0-stage.3) (2025-05-15) diff --git a/package.json b/package.json index 47710596..27d4cb71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.3", + "version": "1.20.0-stage.4", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 94a173990515dc17fcc954baa3542c4645a6fb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Wed, 28 May 2025 14:55:16 -0300 Subject: [PATCH 09/34] fix: revert exported name (presets) --- packages/presets/src/presets/angular/index.ts | 2 +- packages/presets/src/presets/astro/index.ts | 2 +- packages/presets/src/presets/docusaurus/index.ts | 2 +- packages/presets/src/presets/eleventy/index.ts | 2 +- packages/presets/src/presets/emscripten/index.ts | 2 +- packages/presets/src/presets/gatsby/index.ts | 2 +- packages/presets/src/presets/hexo/index.ts | 2 +- packages/presets/src/presets/html/index.ts | 2 +- packages/presets/src/presets/hugo/index.ts | 2 +- packages/presets/src/presets/javascript/index.ts | 2 +- packages/presets/src/presets/jekyll/index.ts | 2 +- packages/presets/src/presets/next/index.ts | 2 +- packages/presets/src/presets/nuxt/index.ts | 2 +- packages/presets/src/presets/preact/index.ts | 2 +- packages/presets/src/presets/qwik/index.ts | 2 +- packages/presets/src/presets/react/index.ts | 2 +- packages/presets/src/presets/rustwasm/index.ts | 2 +- packages/presets/src/presets/stencil/index.ts | 2 +- packages/presets/src/presets/svelte/index.ts | 2 +- packages/presets/src/presets/typescript/index.ts | 2 +- packages/presets/src/presets/vitepress/index.ts | 2 +- packages/presets/src/presets/vue/index.ts | 2 +- packages/presets/src/presets/vuepress/index.ts | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/presets/src/presets/angular/index.ts b/packages/presets/src/presets/angular/index.ts index d6caaf1f..6ad3d670 100644 --- a/packages/presets/src/presets/angular/index.ts +++ b/packages/presets/src/presets/angular/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Angular: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const angular: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/astro/index.ts b/packages/presets/src/presets/astro/index.ts index 3c4d7ee0..c7f67c3e 100644 --- a/packages/presets/src/presets/astro/index.ts +++ b/packages/presets/src/presets/astro/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Astro: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const astro: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/docusaurus/index.ts b/packages/presets/src/presets/docusaurus/index.ts index 766f7c56..4e343548 100644 --- a/packages/presets/src/presets/docusaurus/index.ts +++ b/packages/presets/src/presets/docusaurus/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Docusaurus: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const docusaurus: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/eleventy/index.ts b/packages/presets/src/presets/eleventy/index.ts index 695e74e3..c7cae4ed 100644 --- a/packages/presets/src/presets/eleventy/index.ts +++ b/packages/presets/src/presets/eleventy/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Eleventy: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const eleventy: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/emscripten/index.ts b/packages/presets/src/presets/emscripten/index.ts index e9ee6bdf..d90fcee4 100644 --- a/packages/presets/src/presets/emscripten/index.ts +++ b/packages/presets/src/presets/emscripten/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Emscripten: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const emscripten: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/gatsby/index.ts b/packages/presets/src/presets/gatsby/index.ts index efcb19ad..b9664b16 100644 --- a/packages/presets/src/presets/gatsby/index.ts +++ b/packages/presets/src/presets/gatsby/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Gatsby: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const gatsby: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/hexo/index.ts b/packages/presets/src/presets/hexo/index.ts index 4f9aab3c..c904fdea 100644 --- a/packages/presets/src/presets/hexo/index.ts +++ b/packages/presets/src/presets/hexo/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Hexo: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const hexo: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/html/index.ts b/packages/presets/src/presets/html/index.ts index 2ed87091..5a68e0ed 100644 --- a/packages/presets/src/presets/html/index.ts +++ b/packages/presets/src/presets/html/index.ts @@ -3,4 +3,4 @@ import config from './config'; import handler from './handler'; import metadata from './metadata'; -export const HTML: AzionBuildPreset = { config, metadata, handler }; +export const html: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/hugo/index.ts b/packages/presets/src/presets/hugo/index.ts index 64c10847..e145e93a 100644 --- a/packages/presets/src/presets/hugo/index.ts +++ b/packages/presets/src/presets/hugo/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Hugo: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const hugo: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/javascript/index.ts b/packages/presets/src/presets/javascript/index.ts index a030b2fa..7397d535 100644 --- a/packages/presets/src/presets/javascript/index.ts +++ b/packages/presets/src/presets/javascript/index.ts @@ -2,4 +2,4 @@ import type { AzionBuildPreset } from 'azion/config'; import config from './config'; import metadata from './metadata'; -export const JavaScript: AzionBuildPreset = { config, metadata }; +export const javascript: AzionBuildPreset = { config, metadata }; diff --git a/packages/presets/src/presets/jekyll/index.ts b/packages/presets/src/presets/jekyll/index.ts index 30c9c362..bf2ac691 100644 --- a/packages/presets/src/presets/jekyll/index.ts +++ b/packages/presets/src/presets/jekyll/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Jekyll: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const jekyll: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/next/index.ts b/packages/presets/src/presets/next/index.ts index 2f4b2c97..04b642d3 100644 --- a/packages/presets/src/presets/next/index.ts +++ b/packages/presets/src/presets/next/index.ts @@ -6,4 +6,4 @@ import postbuild from './postbuild'; import prebuild from './prebuild'; ``; -export const Next: AzionBuildPreset = { config, metadata, handler, prebuild, postbuild }; +export const next: AzionBuildPreset = { config, metadata, handler, prebuild, postbuild }; diff --git a/packages/presets/src/presets/nuxt/index.ts b/packages/presets/src/presets/nuxt/index.ts index 70607442..9757eb93 100644 --- a/packages/presets/src/presets/nuxt/index.ts +++ b/packages/presets/src/presets/nuxt/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Nuxt: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const nuxt: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/preact/index.ts b/packages/presets/src/presets/preact/index.ts index da7b74c4..6766e028 100644 --- a/packages/presets/src/presets/preact/index.ts +++ b/packages/presets/src/presets/preact/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; // import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Preact: AzionBuildPreset = { config, metadata, handler }; +export const preact: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/qwik/index.ts b/packages/presets/src/presets/qwik/index.ts index 194b555e..85a666e8 100644 --- a/packages/presets/src/presets/qwik/index.ts +++ b/packages/presets/src/presets/qwik/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Qwik: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const qwik: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/react/index.ts b/packages/presets/src/presets/react/index.ts index 5e345b13..ee9cfa51 100644 --- a/packages/presets/src/presets/react/index.ts +++ b/packages/presets/src/presets/react/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const React: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const react: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/rustwasm/index.ts b/packages/presets/src/presets/rustwasm/index.ts index 4fc8968b..f6836ad3 100644 --- a/packages/presets/src/presets/rustwasm/index.ts +++ b/packages/presets/src/presets/rustwasm/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; // import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const RustWASM: AzionBuildPreset = { config, metadata, handler }; +export const rustwasm: AzionBuildPreset = { config, metadata, handler }; diff --git a/packages/presets/src/presets/stencil/index.ts b/packages/presets/src/presets/stencil/index.ts index 41e351fc..dce41512 100644 --- a/packages/presets/src/presets/stencil/index.ts +++ b/packages/presets/src/presets/stencil/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Stencil: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const stencil: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/svelte/index.ts b/packages/presets/src/presets/svelte/index.ts index 6e693f49..f367ac19 100644 --- a/packages/presets/src/presets/svelte/index.ts +++ b/packages/presets/src/presets/svelte/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const Svelte: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const svelte: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/typescript/index.ts b/packages/presets/src/presets/typescript/index.ts index b5e8cd97..f482a3c7 100644 --- a/packages/presets/src/presets/typescript/index.ts +++ b/packages/presets/src/presets/typescript/index.ts @@ -2,4 +2,4 @@ import type { AzionBuildPreset } from 'azion/config'; import config from './config'; import metadata from './metadata'; -export const TypeScript: AzionBuildPreset = { config, metadata }; +export const typescript: AzionBuildPreset = { config, metadata }; diff --git a/packages/presets/src/presets/vitepress/index.ts b/packages/presets/src/presets/vitepress/index.ts index 64193d54..c4430f21 100644 --- a/packages/presets/src/presets/vitepress/index.ts +++ b/packages/presets/src/presets/vitepress/index.ts @@ -5,4 +5,4 @@ import metadata from './metadata'; import prebuild from './prebuild'; // import postbuild from './postbuild'; -export const VitePress: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const vitepress: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/vue/index.ts b/packages/presets/src/presets/vue/index.ts index 5db21c24..63bc0f7c 100644 --- a/packages/presets/src/presets/vue/index.ts +++ b/packages/presets/src/presets/vue/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const Vue: AzionBuildPreset = { config, handler, metadata, prebuild }; +export const vue: AzionBuildPreset = { config, handler, metadata, prebuild }; diff --git a/packages/presets/src/presets/vuepress/index.ts b/packages/presets/src/presets/vuepress/index.ts index e3380694..4df290a4 100644 --- a/packages/presets/src/presets/vuepress/index.ts +++ b/packages/presets/src/presets/vuepress/index.ts @@ -4,4 +4,4 @@ import handler from './handler'; import metadata from './metadata'; import prebuild from './prebuild'; -export const VuePress: AzionBuildPreset = { config, metadata, handler, prebuild }; +export const vuepress: AzionBuildPreset = { config, metadata, handler, prebuild }; From 710cf7a0028b32d13d884c9ae12ac615b14eedb8 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 28 May 2025 17:56:39 +0000 Subject: [PATCH 10/34] chore(release): 1.20.0-stage.5 [skip ci] ## [1.20.0-stage.5](https://github.com/aziontech/lib/compare/v1.20.0-stage.4...v1.20.0-stage.5) (2025-05-28) ### Bug Fixes * revert exported name (presets) ([94a1739](https://github.com/aziontech/lib/commit/94a173990515dc17fcc954baa3542c4645a6fb59)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40828a74..9a36fb60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.5](https://github.com/aziontech/lib/compare/v1.20.0-stage.4...v1.20.0-stage.5) (2025-05-28) + + +### Bug Fixes + +* revert exported name (presets) ([94a1739](https://github.com/aziontech/lib/commit/94a173990515dc17fcc954baa3542c4645a6fb59)) + ## [1.20.0-stage.4](https://github.com/aziontech/lib/compare/v1.20.0-stage.3...v1.20.0-stage.4) (2025-05-27) diff --git a/package.json b/package.json index 27d4cb71..a140c7ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.4", + "version": "1.20.0-stage.5", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From f779d6af4f2c9fd03fdce45d851f72347c21f3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Wed, 28 May 2025 17:03:13 -0300 Subject: [PATCH 11/34] feat: purge api v4 (#167) --- .../src/configProcessor/helpers/schema.ts | 22 ++++++++----------- .../purgeProcessConfigStrategy.ts | 22 +++++++++---------- packages/config/src/types.ts | 10 ++++----- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 1cbe00d6..4a33d013 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -1011,33 +1011,29 @@ const azionConfigSchema = { enum: ['url', 'cachekey', 'wildcard'], errorMessage: "The 'type' field must be either 'url', 'cachekey' or 'wildcard'.", }, - urls: { + items: { type: 'array', + minItems: 1, items: { type: 'string', - errorMessage: "Each item in 'urls' must be a string.", + errorMessage: "Each item in 'items' must be a string.", }, errorMessage: { - type: "The 'urls' field must be an array of strings.", + type: "The 'items' field must be an array of strings.", + minItems: 'The purge items array cannot be empty. At least one item must be specified.', }, }, - method: { - type: 'string', - enum: ['delete'], - errorMessage: "The 'method' field must be either 'delete'. Default is 'delete'.", - }, layer: { type: 'string', - enum: ['edge_caching', 'l2_caching'], - errorMessage: - "The 'layer' field must be either 'edge_caching' or 'l2_caching'. Default is 'edge_caching'.", + enum: ['edge_cache', 'tiered_cache'], + errorMessage: "The 'layer' field must be either 'edge_cache' or 'tiered_cache'.", }, }, - required: ['type', 'urls'], + required: ['type', 'items'], additionalProperties: false, errorMessage: { additionalProperties: 'No additional properties are allowed in purge items.', - required: "The 'type and urls' fields are required in each purge item.", + required: "The 'type and items' fields are required in each purge item.", }, }, }, diff --git a/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts index f27e52da..d7a77aa1 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts @@ -14,8 +14,8 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { return; } config?.purge.forEach((purge) => { - purge?.urls.forEach((value) => { - if (!value.includes('http://') && !value.includes('https://')) { + purge?.items.forEach((value) => { + if (purge.type === 'url' && !value.includes('http://') && !value.includes('https://')) { throw new Error('The URL must contain the protocol (http:// or https://).'); } @@ -27,12 +27,11 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any const purgeSetting: any = { type: purge.type, - urls: purge.urls || [], - method: purge.method || 'delete', + items: purge.items || [], }; - if (purge?.type === 'cachekey') { - purgeSetting.layer = purge.layer || 'edge_caching'; + if (purge?.layer) { + purgeSetting.layer = purge.layer; } payload.push(purgeSetting); @@ -48,8 +47,8 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { } transformedPayload.purge = []; purgeConfig.forEach((purge) => { - purge.urls.forEach((value: string) => { - if (!value.includes('http://') && !value.includes('https://')) { + purge.items.forEach((value: string) => { + if (purge.type === 'url' && !value.includes('http://') && !value.includes('https://')) { throw new Error('The URL must contain the protocol (http:// or https://).'); } @@ -59,12 +58,11 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { }); const purgeSetting: AzionPurge = { type: purge.type, - urls: purge.urls || [], - method: purge.method || 'delete', + items: purge.items || [], }; - if (purge?.type === 'cachekey') { - purgeSetting.layer = purge.layer || 'edge_caching'; + if (purge?.layer) { + purgeSetting.layer = purge.layer; } transformedPayload.purge!.push(purgeSetting); diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index dde7cd81..118c82cd 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -261,14 +261,12 @@ export type AzionRules = { * Purge configuration for Azion. */ export type AzionPurge = { + /** Items to be purged */ + items: string[]; + /** Cache layer to be purged */ + layer?: 'edge_cache' | 'tiered_cache'; /** Purge type */ type: 'url' | 'cachekey' | 'wildcard'; - /** URLs to be purged */ - urls: string[]; - /** HTTP method for purge request */ - method?: 'delete'; - /** Cache layer to be purged */ - layer?: 'edge_caching' | 'l2_caching'; }; export type PresetInput = string | AzionBuildPreset; From bc082634661a4f5ff3db01516d2450c930acbb90 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 28 May 2025 20:04:30 +0000 Subject: [PATCH 12/34] chore(release): 1.20.0-stage.6 [skip ci] ## [1.20.0-stage.6](https://github.com/aziontech/lib/compare/v1.20.0-stage.5...v1.20.0-stage.6) (2025-05-28) ### Features * purge api v4 (#167) ([f779d6a](https://github.com/aziontech/lib/commit/f779d6af4f2c9fd03fdce45d851f72347c21f3b9)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a36fb60..3193404f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.6](https://github.com/aziontech/lib/compare/v1.20.0-stage.5...v1.20.0-stage.6) (2025-05-28) + + +### Features + +* purge api v4 (#167) ([f779d6a](https://github.com/aziontech/lib/commit/f779d6af4f2c9fd03fdce45d851f72347c21f3b9)) + ## [1.20.0-stage.5](https://github.com/aziontech/lib/compare/v1.20.0-stage.4...v1.20.0-stage.5) (2025-05-28) diff --git a/package.json b/package.json index a140c7ff..b80b34bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.5", + "version": "1.20.0-stage.6", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 118f7bb5ae501df4eb1c8e96fed7a976fcbb7f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Mon, 2 Jun 2025 15:37:00 -0300 Subject: [PATCH 13/34] fix: streamline stream polyfills and enhance process, module, setInterval (#169) --- .../stream/context/stream.context.js | 29 +++--- .../src/polyfills/stream/stream.polyfills.js | 85 +++++++++++------ packages/unenv-preset/src/index.ts | 3 +- .../src/polyfills/node/globals/process.cjs | 6 ++ .../polyfills/node/globals/set-interval.js | 21 +++++ .../unenv-preset/src/polyfills/node/module.js | 94 +++++++++++++------ 6 files changed, 162 insertions(+), 76 deletions(-) create mode 100644 packages/unenv-preset/src/polyfills/node/globals/set-interval.js diff --git a/packages/bundler/src/polyfills/stream/context/stream.context.js b/packages/bundler/src/polyfills/stream/context/stream.context.js index 4f9dec57..787a6f7b 100644 --- a/packages/bundler/src/polyfills/stream/context/stream.context.js +++ b/packages/bundler/src/polyfills/stream/context/stream.context.js @@ -1,21 +1,16 @@ /* eslint-disable */ import stream from 'node:stream'; -export var { Duplex } = stream; -export var { Writable } = stream; -export var { Readable } = stream; -export var { Transform } = stream; -export var { PassThrough } = stream; -export var { Stream } = stream; -export var { prototype } = stream; +const localStream = {}; +export const { Duplex, Writable, Readable, Transform, PassThrough, Stream } = stream; +export const { prototype } = stream; -export default { - Duplex, - Writable, - Readable, - Transform, - PassThrough, - Stream, - stream, - prototype, -}; +localStream.Duplex = Duplex; +localStream.Writable = Writable; +localStream.Readable = Readable; +localStream.Transform = Transform; +localStream.PassThrough = PassThrough; +localStream.Stream = Stream; +localStream.prototype = prototype; + +export default localStream; diff --git a/packages/bundler/src/polyfills/stream/stream.polyfills.js b/packages/bundler/src/polyfills/stream/stream.polyfills.js index f91de692..a6e836da 100644 --- a/packages/bundler/src/polyfills/stream/stream.polyfills.js +++ b/packages/bundler/src/polyfills/stream/stream.polyfills.js @@ -1,30 +1,60 @@ /* eslint-disable */ -/** This polyfill is referenced in #build/bundlers/polyfills/polyfills-manager.js - * +/** * STREAM_CONTEXT is defined in runtime.env.js for use on the local server */ -export var { Duplex } = STREAM_CONTEXT; -export var { Writable } = STREAM_CONTEXT; -export var { Readable } = STREAM_CONTEXT; -export var { Transform } = STREAM_CONTEXT; -export var { PassThrough } = STREAM_CONTEXT; -export var { Stream } = STREAM_CONTEXT; -export var { prototype } = STREAM_CONTEXT; +const localStream = {}; +const { Duplex, Writable, Readable, Transform, PassThrough, Stream } = STREAM_CONTEXT; +export const { prototype } = STREAM_CONTEXT; + +localStream.Duplex = Duplex; +localStream.Writable = Writable; +localStream.Readable = Readable; +localStream.Transform = Transform; +localStream.PassThrough = PassThrough; +localStream.Stream = Stream; +localStream.prototype = prototype; Readable.toWeb = function (readable) { + let onData, onEnd, onError; + let closed = false; const stream = new ReadableStream({ start(controller) { - readable.on('data', (chunk) => { - controller.enqueue(chunk); - }); - readable.on('end', () => { - controller.close(); - }); - readable.on('error', (error) => { - controller.error(error); + onData = (chunk) => { + if (!closed) controller.enqueue(chunk); + }; + onEnd = () => { + if (!closed) { + closed = true; + controller.close(); + } + }; + onError = (error) => { + if (!closed) { + closed = true; + controller.error(error); + } + }; + + readable.on('data', onData); + readable.on('end', onEnd); + readable.on('error', onError); + + readable.on('close', () => { + if (!closed) { + closed = true; + controller.close(); + } }); }, + cancel(reason) { + readable.off('data', onData); + readable.off('end', onEnd); + readable.off('error', onError); + if (typeof readable.destroy === 'function') { + readable.destroy(reason); + } + }, }); return stream; }; @@ -45,12 +75,14 @@ Readable.fromWeb = function (webStream) { }); }; -Writable.toWeb = function (webStream) { +Writable.fromWeb = function (webStream) { const writer = webStream.getWriter(); writer.closed.catch((error) => { - console.error('WritableStream closed with error:', error); - console.error('Error details:', error?.message, error?.stack); + if (error) { + console.error('WritableStream closed with error:', error); + console.error('Error details:', error?.message, error?.stack); + } }); return new Writable({ @@ -92,13 +124,6 @@ Writable.toWeb = function (webStream) { }); }; -export default { - Duplex, - Writable, - Readable, - Transform, - PassThrough, - Stream, - stream: STREAM_CONTEXT, - prototype, -}; +export { Duplex, PassThrough, Readable, Stream, Transform, Writable }; + +export default localStream; diff --git a/packages/unenv-preset/src/index.ts b/packages/unenv-preset/src/index.ts index 69ccad17..a1325a90 100644 --- a/packages/unenv-preset/src/index.ts +++ b/packages/unenv-preset/src/index.ts @@ -12,7 +12,8 @@ export default { __dirname: `${polyfillsPath}/node/globals/path-dirname.js`, __filename: `${polyfillsPath}/node/globals/path-filename.js`, process: `${polyfillsPath}/node/globals/process.cjs`, - performance: `${polyfillsPath}/node/globals/performance.js`, + performance: `unenv/polyfill/performance`, + setInterval: `${polyfillsPath}/node/globals/set-interval.js`, }, alias: { 'azion/utils': 'azion/utils', diff --git a/packages/unenv-preset/src/polyfills/node/globals/process.cjs b/packages/unenv-preset/src/polyfills/node/globals/process.cjs index 6e6e524e..7be5f3ed 100644 --- a/packages/unenv-preset/src/polyfills/node/globals/process.cjs +++ b/packages/unenv-preset/src/polyfills/node/globals/process.cjs @@ -1,5 +1,7 @@ /* eslint-disable */ // shim for using process in browser +globalThis.startTime = globalThis.startTime || Date.now(); + var processShim = (module.exports = {}); /* @@ -189,6 +191,10 @@ processShim.emit = noop; processShim.prependListener = noop; processShim.prependOnceListener = noop; +processShim.uptime = function () { + return (Date.now() - globalThis.startTime) / 1000; +}; + processShim.listeners = function (name) { return []; }; diff --git a/packages/unenv-preset/src/polyfills/node/globals/set-interval.js b/packages/unenv-preset/src/polyfills/node/globals/set-interval.js new file mode 100644 index 00000000..ac4a6aca --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/globals/set-interval.js @@ -0,0 +1,21 @@ +const _setInterval = globalThis.setInterval; +const _clearInterval = globalThis.clearInterval; + +globalThis.setInterval = (...args) => { + const id = _setInterval(...args); + if (typeof id === 'object' && id !== null) { + // this is necessary for compatibility with the Sentry library and node's timers + if (typeof id.unref !== 'function') { + id.unref = () => {}; + } + return id; + } + return { + id, + unref: () => {}, + ref: () => {}, + clear: () => _clearInterval(id), + }; +}; + +export default globalThis.setInterval; diff --git a/packages/unenv-preset/src/polyfills/node/module.js b/packages/unenv-preset/src/polyfills/node/module.js index 506d7038..35c2b9ea 100644 --- a/packages/unenv-preset/src/polyfills/node/module.js +++ b/packages/unenv-preset/src/polyfills/node/module.js @@ -8,7 +8,7 @@ function unimplemented() { throw new Error('Not implemented yet!'); } -const builtinModules = [ +var builtinModules = [ '_http_agent', '_http_client', '_http_common', @@ -84,34 +84,72 @@ function _nodeModulePaths(...args) { /* EMPTY */ } -// Crie um objeto para exportação -const moduleExports = { - builtinModules: builtinModules, - _cache: null, - _pathCache: null, - _extensions: null, - globalPaths: null, - _debug: unimplemented, - _findPath: unimplemented, - _nodeModulePaths: _nodeModulePaths, - _resolveLookupPaths: unimplemented, - _load: _load, +function _resolveFilename(...args) { + /* EMPTY */ +} + +const Module = {}; + +// Adicione as propriedades estáticas esperadas +Module.builtinModules = builtinModules; +Module._cache = null; +Module._pathCache = null; +Module._extensions = null; +Module.globalPaths = null; +Module._debug = unimplemented; +Module._findPath = unimplemented; +Module._nodeModulePaths = _nodeModulePaths; +Module._resolveLookupPaths = unimplemented; +Module._load = _load; +Module._resolveFilename = _resolveFilename; +Module.createRequireFromPath = unimplemented; +Module.createRequire = createRequire; +Module._initPaths = unimplemented; +Module._preloadModules = unimplemented; +Module.syncBuiltinESMExports = unimplemented; +Module.runMain = unimplemented; +Module.findSourceMap = unimplemented; +Module.SourceMap = unimplemented; +Module.require = unimplemented; +const _prototype = { + require: unimplemented, + resolve: unimplemented, + paths: [], + id: '', + filename: '', + loaded: false, + children: [], + exports: {}, + _compile: unimplemented, _resolveFilename: unimplemented, - createRequireFromPath: unimplemented, - createRequire: createRequire, - _initPaths: unimplemented, - _preloadModules: unimplemented, - syncBuiltinESMExports: unimplemented, - Module: unimplemented, - runMain: unimplemented, - findSourceMap: unimplemented, - SourceMap: unimplemented, }; -Object.defineProperty(moduleExports, '_resolveFilename', { - value: unimplemented, - writable: true, - configurable: true, -}); +export default Module; + +export var _cache = null, + _pathCache = null, + _extensions = null, + globalPaths = null; + +export { + unimplemented as _debug, + unimplemented as _findPath, + unimplemented as _initPaths, + unimplemented as _load, + unimplemented as _nodeModulePaths, + unimplemented as _preloadModules, + unimplemented as _resolveFilename, + unimplemented as _resolveLookupPaths, + builtinModules, + createRequire as createRequire, + createRequire as createRequireFromPath, + unimplemented as findSourceMap, + Module, + _prototype as prototype, + unimplemented as require, + unimplemented as runMain, + unimplemented as SourceMap, + unimplemented as syncBuiltinESMExports, +}; -export default moduleExports; +/* eslint-enable */ From 41b79db92390cf36c0b19d90b73cc039e8ca8ee2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 2 Jun 2025 18:38:16 +0000 Subject: [PATCH 14/34] chore(release): 1.20.0-stage.7 [skip ci] ## [1.20.0-stage.7](https://github.com/aziontech/lib/compare/v1.20.0-stage.6...v1.20.0-stage.7) (2025-06-02) ### Bug Fixes * streamline stream polyfills and enhance process, module, setInterval (#169) ([118f7bb](https://github.com/aziontech/lib/commit/118f7bb5ae501df4eb1c8e96fed7a976fcbb7f5d)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3193404f..44857b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.7](https://github.com/aziontech/lib/compare/v1.20.0-stage.6...v1.20.0-stage.7) (2025-06-02) + + +### Bug Fixes + +* streamline stream polyfills and enhance process, module, setInterval (#169) ([118f7bb](https://github.com/aziontech/lib/commit/118f7bb5ae501df4eb1c8e96fed7a976fcbb7f5d)) + ## [1.20.0-stage.6](https://github.com/aziontech/lib/compare/v1.20.0-stage.5...v1.20.0-stage.6) (2025-05-28) diff --git a/package.json b/package.json index b80b34bc..26bdca27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.6", + "version": "1.20.0-stage.7", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From dc33e5a9cfe9bd069d5bb5fd61fe026ddde53dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 3 Jun 2025 10:40:53 -0300 Subject: [PATCH 15/34] fix: api v4 (#168) * feat: purge api v4 * refactor: input_value -> argument * refactor: behavior: target -> argument * fix: return empty array instead of object when no edgeFunctions are present --- .../src/rules-engine/services/types.ts | 4 +- packages/client/src/types.ts | 2 +- packages/config/README.md | 6 +- .../converterJsonConfig/index.test.ts | 126 +++++++++--------- .../helpers/azion.config.example.ts | 10 +- .../src/configProcessor/helpers/behaviors.ts | 60 ++++----- .../src/configProcessor/helpers/schema.ts | 8 +- .../configProcessor/helpers/schemaManifest.ts | 20 +-- .../processConfig/index.test.ts | 92 ++++++------- .../application/rulesProcessConfigStrategy.ts | 28 ++-- .../functionsProcessConfigStrategy.ts | 6 +- .../firewallProcessConfigStrategy.test.ts | 8 +- .../secure/firewallProcessConfigStrategy.ts | 14 +- packages/config/src/types.ts | 2 +- 14 files changed, 193 insertions(+), 193 deletions(-) diff --git a/packages/applications/src/rules-engine/services/types.ts b/packages/applications/src/rules-engine/services/types.ts index 99ce5d54..c5c78b60 100644 --- a/packages/applications/src/rules-engine/services/types.ts +++ b/packages/applications/src/rules-engine/services/types.ts @@ -43,7 +43,7 @@ export interface Criterion { variable: string; operator: string; conditional: 'if' | 'and' | 'or'; - input_value: string; + argument: string; } export interface ApiCreateRulePayload { @@ -107,7 +107,7 @@ export interface Criterion { variable: string; operator: string; conditional: 'if' | 'and' | 'or'; - input_value: string; + argument: string; } export interface ApiCreateRulePayload { diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index d085dae4..1491285b 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -172,7 +172,7 @@ export interface AzionClient { * const { data: newRule } = await app.rules.request.createRule({ * data: { * name: 'My Rule', - * behaviors: [{ name: 'set_origin', target: newOrigin.id }], + * behaviors: [{ name: 'set_origin', argument: newOrigin.id }], * criteria: [{ condition: 'starts_with', variable: '${uri}', input: '/api' }] * } * }); diff --git a/packages/config/README.md b/packages/config/README.md index 49213a8b..60e88df5 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -129,7 +129,7 @@ const config = defineConfig({ variable: 'remote_addr', operator: 'in', conditional: 'if', - inputValue: 'suspicious_ips', + argument: 'suspicious_ips', }, ], behavior: { @@ -145,7 +145,7 @@ const config = defineConfig({ variable: 'uri', operator: 'starts_with', conditional: 'if', - inputValue: '/api/', + argument: '/api/', }, ], behavior: { @@ -597,7 +597,7 @@ Type definition for the response rule configuration. - `variable: RuleVariable` - Variable to be evaluated. - `conditional: RuleConditional` - Conditional type. - `operator: RuleOperatorWithValue | RuleOperatorWithoutValue` - Comparison operator. - - `inputValue?: string` - Input value for comparison (required for operators with value). + - `argument?: string` - Input value for comparison (required for operators with value). ### `AzionWaf` diff --git a/packages/config/src/configProcessor/converterJsonConfig/index.test.ts b/packages/config/src/configProcessor/converterJsonConfig/index.test.ts index b3687906..70d02d23 100644 --- a/packages/config/src/configProcessor/converterJsonConfig/index.test.ts +++ b/packages/config/src/configProcessor/converterJsonConfig/index.test.ts @@ -201,7 +201,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -227,7 +227,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], behavior: { @@ -250,7 +250,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -282,7 +282,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule responds with a file from the origin storage.', @@ -309,7 +309,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -333,7 +333,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule rewrites the request path.', @@ -357,7 +357,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -380,7 +380,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule delivers the request.', @@ -404,7 +404,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -428,7 +428,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule sets a cookie.', @@ -452,7 +452,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -484,7 +484,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule sets multiple headers.', @@ -508,7 +508,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -538,7 +538,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule sets the cache.', @@ -562,7 +562,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -596,7 +596,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule sets the cache.', @@ -624,7 +624,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -648,7 +648,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule forwards the cookie.', @@ -679,7 +679,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -703,7 +703,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule runs a function.', @@ -729,7 +729,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -752,7 +752,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule enables GZIP compression.', @@ -778,7 +778,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -801,7 +801,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule bypasses the cache.', @@ -827,7 +827,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${scheme}`, operator: 'matches', conditional: 'if', - input_value: 'http', + argument: 'http', }, ], ], @@ -852,7 +852,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${scheme}`, operator: 'matches', conditional: 'if', - inputValue: 'http', + argument: 'http', }, ], description: 'This rule redirects HTTP to HTTPS.', @@ -877,7 +877,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -905,7 +905,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule captures the request.', @@ -935,7 +935,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -963,7 +963,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule captures the request.', @@ -990,7 +990,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1014,7 +1014,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule the request.', @@ -1040,7 +1040,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/images', + argument: '/images', }, ], ], @@ -1064,7 +1064,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/images', + argument: '/images', }, ], description: 'This rule the request.', @@ -1089,7 +1089,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1121,7 +1121,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule sets multiple headers.', @@ -1146,7 +1146,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/login', + argument: '/login', }, ], ], @@ -1170,7 +1170,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/login', + argument: '/login', }, ], description: 'This rule the request.', @@ -1198,7 +1198,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1223,7 +1223,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], active: false, @@ -1248,7 +1248,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1274,7 +1274,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], behavior: { @@ -1298,7 +1298,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - input_value: '200', + argument: '200', }, ], ], @@ -1322,7 +1322,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - inputValue: '200', + argument: '200', }, ], active: false, @@ -1347,7 +1347,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - input_value: '200', + argument: '200', }, ], ], @@ -1371,7 +1371,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - inputValue: '200', + argument: '200', }, ], description: 'This rule sets a header.', @@ -1395,7 +1395,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - input_value: '200', + argument: '200', }, ], ], @@ -1418,7 +1418,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - inputValue: '200', + argument: '200', }, ], description: 'This rule enables GZIP compression.', @@ -1443,7 +1443,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1468,7 +1468,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule filters the cookie.', @@ -1493,7 +1493,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - input_value: '200', + argument: '200', }, ], ], @@ -1518,7 +1518,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - inputValue: '200', + argument: '200', }, ], description: 'This rule filters the header.', @@ -1549,7 +1549,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -1573,7 +1573,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - inputValue: '/test', + argument: '/test', }, ], description: 'This rule runs a function.', @@ -1599,7 +1599,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'equals', conditional: 'if', - input_value: '/', + argument: '/', }, ], ], @@ -1624,7 +1624,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'equals', conditional: 'if', - inputValue: '/', + argument: '/', }, ], description: 'This rule delivers the response.', @@ -1909,7 +1909,7 @@ describe('convertJsonConfigToObject', () => { variable: 'invalid_variable', operator: 'is_equal', conditional: 'if', - input_value: 'test', + argument: 'test', }, }, ], @@ -1935,7 +1935,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'is_equal', conditional: 'if', - input_value: '/test', + argument: '/test', }, behavior: { name: 'invalid_behavior', @@ -1965,7 +1965,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'is_equal', conditional: 'if', - input_value: '/test', + argument: '/test', }, behavior: { name: 'set_rate_limit', @@ -2132,7 +2132,7 @@ describe('convertJsonConfigToObject', () => { variable: 'invalid_variable', operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -2169,7 +2169,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'matches', conditional: 'if', - input_value: '/test', + argument: '/test', }, ], ], @@ -2190,7 +2190,7 @@ describe('convertJsonConfigToObject', () => { ); }); - it('should throw an error when input_value is missing for operator that requires it', () => { + it('should throw an error when argument is missing for operator that requires it', () => { const jsonConfig = { application: [ { @@ -2222,7 +2222,7 @@ describe('convertJsonConfigToObject', () => { }; expect(() => convertJsonConfigToObject(JSON.stringify(jsonConfig))).toThrow( - "The operator 'matches' requires an input_value.", + "The operator 'matches' requires an argument.", ); }); }); diff --git a/packages/config/src/configProcessor/helpers/azion.config.example.ts b/packages/config/src/configProcessor/helpers/azion.config.example.ts index 2f890e76..25a0bc5f 100644 --- a/packages/config/src/configProcessor/helpers/azion.config.example.ts +++ b/packages/config/src/configProcessor/helpers/azion.config.example.ts @@ -241,7 +241,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -258,7 +258,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -274,7 +274,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/login', + argument: '^/login', }, ], behavior: { @@ -356,7 +356,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -372,7 +372,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { diff --git a/packages/config/src/configProcessor/helpers/behaviors.ts b/packages/config/src/configProcessor/helpers/behaviors.ts index 8138ee5c..d7ef4125 100644 --- a/packages/config/src/configProcessor/helpers/behaviors.ts +++ b/packages/config/src/configProcessor/helpers/behaviors.ts @@ -10,7 +10,7 @@ export const requestBehaviors = { return { name: 'set_edge_connector', - target: connector.name, + argument: connector.name, }; }, }, @@ -19,7 +19,7 @@ export const requestBehaviors = { const behaviors = []; behaviors.push({ name: 'rewrite_request', - target: value, + argument: value, }); return behaviors; }, @@ -27,20 +27,20 @@ export const requestBehaviors = { deliver: { transform: () => ({ name: 'deliver', - target: null, + argument: null, }), }, setCookie: { transform: (value: any) => ({ name: 'add_request_cookie', - target: value, + argument: value, }), }, setHeaders: { transform: (value: any) => value.map((header: any) => ({ name: 'add_request_header', - target: header, + argument: header, })), }, setCache: { @@ -48,7 +48,7 @@ export const requestBehaviors = { if (typeof value === 'string') { return { name: 'set_cache_policy', - target: value, + argument: value, }; } if (typeof value === 'object') { @@ -60,7 +60,7 @@ export const requestBehaviors = { payloadCDN.cache.push(cacheSetting); return { name: 'set_cache_policy', - target: value.name, + argument: value.name, }; } return undefined; @@ -71,7 +71,7 @@ export const requestBehaviors = { if (value) { return { name: 'forward_cookies', - target: null, + argument: null, }; } return undefined; @@ -80,7 +80,7 @@ export const requestBehaviors = { runFunction: { transform: (value: string) => ({ name: 'run_function', - target: value, + argument: value, }), }, enableGZIP: { @@ -88,7 +88,7 @@ export const requestBehaviors = { if (value) { return { name: 'enable_gzip', - target: '', + argument: '', }; } return undefined; @@ -99,7 +99,7 @@ export const requestBehaviors = { if (value) { return { name: 'bypass_cache_phase', - target: null, + argument: null, }; } return undefined; @@ -110,7 +110,7 @@ export const requestBehaviors = { if (value) { return { name: 'redirect_http_to_https', - target: null, + argument: null, }; } return undefined; @@ -119,19 +119,19 @@ export const requestBehaviors = { redirectTo301: { transform: (value: any) => ({ name: 'redirect_to_301', - target: value, + argument: value, }), }, redirectTo302: { transform: (value: any) => ({ name: 'redirect_to_302', - target: value, + argument: value, }), }, capture: { transform: (value: any) => ({ name: 'capture_match_groups', - target: { + argument: { regex: value.match, captured_array: value.captured, subject: `\${${value.subject ?? 'uri'}}`, @@ -141,13 +141,13 @@ export const requestBehaviors = { filterCookie: { transform: (value: any) => ({ name: 'filter_request_cookie', - target: value, + argument: value, }), }, filterHeader: { transform: (value: any) => ({ name: 'filter_request_header', - target: value, + argument: value, }), }, noContent: { @@ -155,7 +155,7 @@ export const requestBehaviors = { if (value) { return { name: 'no_content', - target: null, + argument: null, }; } return undefined; @@ -166,7 +166,7 @@ export const requestBehaviors = { if (value) { return { name: 'optimize_images', - target: null, + argument: null, }; } return undefined; @@ -177,7 +177,7 @@ export const requestBehaviors = { if (value) { return { name: 'deny', - target: null, + argument: null, }; } return undefined; @@ -188,14 +188,14 @@ export const responseBehaviors = { setCookie: { transform: (value: any) => ({ name: 'set_cookie', - target: value, + argument: value, }), }, setHeaders: { transform: (value: any) => value.map((header: any) => ({ name: 'add_response_header', - target: header, + argument: header, })), }, enableGZIP: { @@ -203,7 +203,7 @@ export const responseBehaviors = { if (value) { return { name: 'enable_gzip', - target: '', + argument: '', }; } return undefined; @@ -212,37 +212,37 @@ export const responseBehaviors = { filterCookie: { transform: (value: any) => ({ name: 'filter_response_cookie', - target: value, + argument: value, }), }, filterHeader: { transform: (value: any) => ({ name: 'filter_response_header', - target: value, + argument: value, }), }, runFunction: { transform: (value: string) => ({ name: 'run_function', - target: value, + argument: value, }), }, redirectTo301: { transform: (value: any) => ({ name: 'redirect_to_301', - target: value, + argument: value, }), }, redirectTo302: { transform: (value: any) => ({ name: 'redirect_to_302', - target: value, + argument: value, }), }, capture: { transform: (value: any) => ({ name: 'capture_match_groups', - target: { + argument: { regex: value.match, captured_array: value.captured, subject: `\${${value.subject ?? 'uri'}}`, @@ -254,7 +254,7 @@ export const responseBehaviors = { if (value) { return { name: 'deliver', - target: null, + argument: null, }; } return undefined; diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 4a33d013..90d7e5b8 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -64,11 +64,11 @@ const criteriaBaseSchema = { }, }, then: { - required: ['inputValue'], + required: ['argument'], properties: { - inputValue: { + argument: { type: 'string', - errorMessage: "The 'inputValue' field must be a string", + errorMessage: "The 'argument' field must be a string", }, }, }, @@ -81,7 +81,7 @@ const criteriaBaseSchema = { }, then: { not: { - required: ['inputValue'], + required: ['argument'], }, }, }, diff --git a/packages/config/src/configProcessor/helpers/schemaManifest.ts b/packages/config/src/configProcessor/helpers/schemaManifest.ts index abe84d3b..481c97cc 100644 --- a/packages/config/src/configProcessor/helpers/schemaManifest.ts +++ b/packages/config/src/configProcessor/helpers/schemaManifest.ts @@ -212,9 +212,9 @@ const schemaFirewallRuleCriteria = { enum: FIREWALL_RULE_CONDITIONALS, errorMessage: "The 'conditional' field must be one of: if, and, or", }, - input_value: { + argument: { type: 'string', - errorMessage: "The 'input_value' field must be a string.", + errorMessage: "The 'argument' field must be a string.", }, }, required: ['variable', 'operator', 'conditional'], @@ -418,9 +418,9 @@ const schemaFunctionManifest = { type: 'string', errorMessage: "The 'name' field must be a string", }, - target: { + arguemnt: { type: 'string', - errorMessage: "The 'target' field must be a string", + errorMessage: "The 'argument' field must be a string", }, args: { type: 'object', @@ -594,9 +594,9 @@ const schemaApplicationRules = { enum: RULE_BEHAVIOR_NAMES, errorMessage: "The 'name' field must be a valid behavior name.", }, - target: { + argument: { oneOf: [{ type: 'string' }, { type: 'null' }], - errorMessage: "The 'target' must be a string or null.", + errorMessage: "The 'argument' must be a string or null.", }, }, required: ['name'], @@ -625,9 +625,9 @@ const schemaApplicationRules = { enum: RULE_CONDITIONALS, errorMessage: "The 'conditional' field must be one of: if, and, or.", }, - input_value: { + argument: { type: 'string', - errorMessage: "The 'input_value' field must be a string.", + errorMessage: "The 'argument' field must be a string.", }, }, required: ['variable', 'operator', 'conditional'], @@ -638,8 +638,8 @@ const schemaApplicationRules = { properties: { operator: { enum: RULE_OPERATORS_WITH_VALUE }, }, - required: ['input_value'], - errorMessage: "The operator 'matches' requires an input_value.", + required: ['argument'], + errorMessage: "The operator 'matches' requires an argument.", }, ], }, diff --git a/packages/config/src/configProcessor/processConfig/index.test.ts b/packages/config/src/configProcessor/processConfig/index.test.ts index baa195c9..9512a509 100644 --- a/packages/config/src/configProcessor/processConfig/index.test.ts +++ b/packages/config/src/configProcessor/processConfig/index.test.ts @@ -84,7 +84,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'rewrite_request', - target: '/new-path', + argument: '/new-path', }), ]), ); @@ -171,14 +171,14 @@ describe('processConfig', () => { criteria: expect.arrayContaining([ expect.arrayContaining([ expect.objectContaining({ - input_value: '/api', + argument: '/api', }), ]), ]), behaviors: expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - target: 'my origin storage', + argument: 'my origin storage', }), ]), }), @@ -246,7 +246,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'forward_cookies', - target: null, // Updated from 'params' to 'target' + argument: null, // Updated from 'params' to 'argument' }), ]), ); @@ -323,7 +323,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - target: 'my origin storage', + argument: 'my origin storage', }), ]), ); @@ -393,7 +393,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'run_function', - target: 'handler', + argument: 'handler', }), ]), ); @@ -547,7 +547,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_request_cookie', - target: 'sessionId=abc123', + argument: 'sessionId=abc123', }), ]), ); @@ -573,7 +573,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_request_header', - target: 'Authorization: Bearer abc123', + argument: 'Authorization: Bearer abc123', }), ]), ); @@ -962,7 +962,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - target: 'my single origin', + argument: 'my single origin', }), ]), ); @@ -1003,7 +1003,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'bypass_cache_phase', - target: null, + argument: null, }), ]), ); @@ -1029,7 +1029,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'redirect_to_301', - target: 'https://example.com', + argument: 'https://example.com', }), ]), ); @@ -1055,7 +1055,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'redirect_to_302', - target: 'https://example.com', + argument: 'https://example.com', }), ]), ); @@ -1085,7 +1085,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'capture_match_groups', - target: { + argument: { regex: '^/user/(.*)', captured_array: 'userId', // eslint-disable-next-line no-template-curly-in-string @@ -1116,7 +1116,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'filter_response_cookie', - target: '_cookie', + argument: '_cookie', }), ]), ); @@ -1142,7 +1142,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - target: 'X-Test-Header: value', + argument: 'X-Test-Header: value', }), ]), ); @@ -1168,11 +1168,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - target: 'X-Frame-Options: DENY', + argument: 'X-Frame-Options: DENY', }), expect.objectContaining({ name: 'add_response_header', - target: "Content-Security-Policy: default-src 'self'", + argument: "Content-Security-Policy: default-src 'self'", }), ]), ); @@ -1198,7 +1198,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'enable_gzip', - target: '', + argument: '', }), ]), ); @@ -1326,14 +1326,14 @@ describe('processConfig', () => { expect(result.rules[0].behaviors).toEqual([ expect.objectContaining({ name: 'add_request_header', - target: 'Authorization: Bearer abc123', + argument: 'Authorization: Bearer abc123', }), expect.objectContaining({ name: 'deliver', }), expect.objectContaining({ name: 'set_origin', - target: 'my origin storage', + argument: 'my origin storage', }), ]); }); @@ -1387,11 +1387,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - target: 'legacy origin', + argument: 'legacy origin', }), expect.objectContaining({ name: 'add_request_header', - target: 'Authorization: Bearer legacy', + argument: 'Authorization: Bearer legacy', }), ]), ); @@ -1416,11 +1416,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - target: 'X-Legacy-Header: legacy', + argument: 'X-Legacy-Header: legacy', }), expect.objectContaining({ name: 'enable_gzip', - target: '', + argument: '', }), ]), ); @@ -1456,11 +1456,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - target: 'mixed origin', + argument: 'mixed origin', }), expect.objectContaining({ name: 'add_request_header', - target: 'Authorization: Bearer mixed', + argument: 'Authorization: Bearer mixed', }), ]), ); @@ -1487,11 +1487,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - target: 'X-Mixed-Header: mixed', + argument: 'X-Mixed-Header: mixed', }), expect.objectContaining({ name: 'enable_gzip', - target: '', + argument: '', }), ]), ); @@ -1747,7 +1747,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -1765,7 +1765,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - input_value: '^/', + argument: '^/', }, ], ]); @@ -1783,7 +1783,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - input_value: '^/', + argument: '^/', }, ], behavior: { @@ -1816,13 +1816,13 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, { variable: '${device_group}', operator: 'is_equal', conditional: 'and', - inputValue: 'mobile', + argument: 'mobile', }, ], behavior: { @@ -1840,13 +1840,13 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - input_value: '^/', + argument: '^/', }, { variable: '${device_group}', operator: 'is_equal', conditional: 'and', - input_value: 'mobile', + argument: 'mobile', }, ], ]); @@ -1904,7 +1904,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -1921,11 +1921,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'filter_request_header', - target: 'X-Test-Header', + argument: 'X-Test-Header', }), expect.objectContaining({ name: 'filter_request_cookie', - target: '_cookie', + argument: '_cookie', }), ]), ); @@ -1944,7 +1944,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -1960,7 +1960,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'no_content', - target: null, + argument: null, }), ]), ); @@ -1979,7 +1979,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/', + argument: '^/', }, ], behavior: { @@ -1995,7 +1995,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'deliver', - target: null, + argument: null, }), ]), ); @@ -2014,7 +2014,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/images', + argument: '^/images', }, ], behavior: { @@ -2030,7 +2030,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'optimize_images', - target: null, + argument: null, }), ]), ); @@ -2049,7 +2049,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - inputValue: '^/login', + argument: '^/login', }, ], behavior: { @@ -2065,7 +2065,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'deny', - target: null, + argument: null, }), ]), ); diff --git a/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts index 0b51d77b..f85595f8 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts @@ -90,12 +90,12 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { criteria: rule.criteria ? [ rule.criteria.map((criterion) => { - const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, - ...(isWithValue && { input_value: inputValue }), + ...(isWithValue && { argument: argument }), }; }), ] @@ -105,7 +105,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { variable: rule.variable?.startsWith('${') ? rule.variable : `\${${rule.variable ?? 'uri'}}`, operator: 'matches', conditional: 'if', - input_value: rule.match, + argument: rule.match, }, ], ], @@ -127,12 +127,12 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { criteria: rule.criteria ? [ rule.criteria.map((criterion) => { - const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, - ...(isWithValue && { input_value: inputValue }), + ...(isWithValue && { argument: argument }), }; }), ] @@ -142,7 +142,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { variable: rule.variable?.startsWith('${') ? rule.variable : `\${${rule.variable ?? 'uri'}}`, operator: 'matches', conditional: 'if', - input_value: rule.match, + argument: rule.match, }, ], ], @@ -208,11 +208,11 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) ? // eslint-disable-next-line @typescript-eslint/no-explicit-any rule.criteria[0].map((criterion: any) => { - const isWithValue = 'input_value' in criterion; - const { input_value, ...rest } = criterion; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion; return { ...rest, - ...(isWithValue && { inputValue: input_value }), + ...(isWithValue && { argument: argument }), }; }) : [], @@ -227,11 +227,11 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) ? // eslint-disable-next-line @typescript-eslint/no-explicit-any rule.criteria[0].map((criterion: any) => { - const isWithValue = 'input_value' in criterion; - const { input_value, ...rest } = criterion; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion; return { ...rest, - ...(isWithValue && { inputValue: input_value }), + ...(isWithValue && { argument: argument }), }; }) : [], diff --git a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts index 9a075dc4..1659329e 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts @@ -17,7 +17,7 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { transformToManifest(config: AzionConfig) { if (!Array.isArray(config?.edgeFunctions) || config?.edgeFunctions.length === 0) { - return {}; + return []; } return config.edgeFunctions.map((func) => { @@ -28,7 +28,7 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { return { name: func.name, - target: func.path, + argument: func.path, args: func.args || {}, bindings: func.bindings ? { @@ -54,7 +54,7 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { transformedPayload.edgeFunctions = payload.functions.map((func: any) => { const config = { name: func.name, - path: func.target, + path: func.argument, args: func.args || {}, bindings: func.bindings ? { diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts index 9f9c50e9..a39280f1 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts @@ -32,7 +32,7 @@ describe('FirewallProcessConfigStrategy', () => { variable: 'uri', conditional: 'if', operator: 'matches', - inputValue: '/test', + argument: '/test', }, ], behavior: { @@ -68,7 +68,7 @@ describe('FirewallProcessConfigStrategy', () => { variable: 'uri', conditional: 'if', operator: 'matches', - input_value: '/test', + argument: '/test', }, ], ], @@ -136,7 +136,7 @@ describe('FirewallProcessConfigStrategy', () => { variable: '${uri}', conditional: 'if', operator: 'matches', - input_value: '/test', + argument: '/test', }, ], ], @@ -168,7 +168,7 @@ describe('FirewallProcessConfigStrategy', () => { variable: '${uri}', conditional: 'if', operator: 'matches', - inputValue: '/test', + argument: '/test', }, ], behavior: { diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts index c8a839ca..ae3a48bb 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts @@ -40,12 +40,12 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { criteria: rule.criteria ? [ rule.criteria.map((criterion) => { - const isWithValue = 'inputValue' in criterion; - const { inputValue, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable, - ...(isWithValue && { input_value: inputValue }), + ...(isWithValue && { argument: argument }), }; }), ] @@ -55,7 +55,7 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { variable: rule.variable, operator: 'matches', conditional: 'if', - input_value: rule.match, + argument: rule.match, }, ], ], @@ -156,11 +156,11 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { criteria: // eslint-disable-next-line @typescript-eslint/no-explicit-any rule.criteria?.[0].map((criterion: any) => { - const isWithValue = 'input_value' in criterion; - const { input_value, ...rest } = criterion; + const isWithValue = 'argument' in criterion; + const { argument, ...rest } = criterion; return { ...rest, - ...(isWithValue && { inputValue: input_value }), + ...(isWithValue && { argument: argument }), }; }) || [], }; diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 118c82cd..6ffd53e1 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -109,7 +109,7 @@ export type AzionRuleCriteriaWithValue = AzionRuleCriteriaBase & { /** Operator for comparison that requires input value */ operator: RuleOperatorWithValue; /** Input value for comparison */ - inputValue: string; + argument: string; }; export type AzionRuleCriteriaWithoutValue = AzionRuleCriteriaBase & { From 5cf622e9fb91d026768b07b523df47dd2873495e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 3 Jun 2025 13:42:15 +0000 Subject: [PATCH 16/34] chore(release): 1.20.0-stage.8 [skip ci] ## [1.20.0-stage.8](https://github.com/aziontech/lib/compare/v1.20.0-stage.7...v1.20.0-stage.8) (2025-06-03) ### Bug Fixes * api v4 (#168) ([dc33e5a](https://github.com/aziontech/lib/commit/dc33e5a9cfe9bd069d5bb5fd61fe026ddde53dcb)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44857b11..cf888159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.8](https://github.com/aziontech/lib/compare/v1.20.0-stage.7...v1.20.0-stage.8) (2025-06-03) + + +### Bug Fixes + +* api v4 (#168) ([dc33e5a](https://github.com/aziontech/lib/commit/dc33e5a9cfe9bd069d5bb5fd61fe026ddde53dcb)) + ## [1.20.0-stage.7](https://github.com/aziontech/lib/compare/v1.20.0-stage.6...v1.20.0-stage.7) (2025-06-02) diff --git a/package.json b/package.json index 26bdca27..bbdbf85d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.7", + "version": "1.20.0-stage.8", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 329f4c41af43f8bf9ccede25a14605a8f34bdc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Fri, 6 Jun 2025 15:04:39 -0300 Subject: [PATCH 17/34] fix: polyfills for Node.js compatibility (#170) --- .../bundler/src/polyfills/promises/index.js | 2 +- packages/unenv-preset/src/index.ts | 2 + .../unenv-preset/src/polyfills/node/https.js | 122 +++++++++++----- .../src/polyfills/node/internal/_internal.js | 11 ++ .../polyfills/node/internal/util/inherits.js | 15 ++ .../node/internal/util/legacy-types.js | 51 +++++++ .../src/polyfills/node/internal/util/log.js | 64 +++++++++ .../src/polyfills/node/internal/util/mime.js | 45 ++++++ .../polyfills/node/internal/util/promisify.js | 25 ++++ .../src/polyfills/node/internal/util/types.js | 131 ++++++++++++++++++ .../unenv-preset/src/polyfills/node/util.js | 130 +++++++++++++++++ 11 files changed, 562 insertions(+), 36 deletions(-) create mode 100644 packages/unenv-preset/src/polyfills/node/internal/_internal.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/inherits.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/legacy-types.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/log.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/mime.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/promisify.js create mode 100644 packages/unenv-preset/src/polyfills/node/internal/util/types.js create mode 100644 packages/unenv-preset/src/polyfills/node/util.js diff --git a/packages/bundler/src/polyfills/promises/index.js b/packages/bundler/src/polyfills/promises/index.js index df1d92fd..ce97339c 100644 --- a/packages/bundler/src/polyfills/promises/index.js +++ b/packages/bundler/src/polyfills/promises/index.js @@ -4,4 +4,4 @@ */ import promisesContext from './context/index.js'; -export default { promisesContext }; +export default promisesContext; diff --git a/packages/unenv-preset/src/index.ts b/packages/unenv-preset/src/index.ts index a1325a90..73b30fe0 100644 --- a/packages/unenv-preset/src/index.ts +++ b/packages/unenv-preset/src/index.ts @@ -26,6 +26,8 @@ export default { module: `${polyfillsPath}/node/module.js`, string_decoder: 'string_decoder/lib/string_decoder.js', timers: 'timers-browserify/', + util: `${polyfillsPath}/node/util.js`, + 'util/types': `${polyfillsPath}/node/internal/util/types.js`, }, external: ['node:async_hooks', 'node:fs/promises', 'node:stream', 'node:crypto'], polyfill: [ diff --git a/packages/unenv-preset/src/polyfills/node/https.js b/packages/unenv-preset/src/polyfills/node/https.js index bc7f2ed8..20ff93fd 100644 --- a/packages/unenv-preset/src/polyfills/node/https.js +++ b/packages/unenv-preset/src/polyfills/node/https.js @@ -1,3 +1,4 @@ +/* eslint-disable */ // https://nodejs.org/api/https.html import { EventEmitter } from 'node:events'; @@ -41,47 +42,54 @@ export const get = notImplemented('https.get'); export const createServer = notImplemented('https.createServer'); -export const request = (options, callback) => { - let fullUrl; - let headers = {}; - let method; +export const request = (urlOrOptions, optionsOrCallback, maybeCallback) => { + let url, options, callback; + if (typeof urlOrOptions === 'string' || urlOrOptions instanceof URL) { + url = urlOrOptions; + if (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) { + options = optionsOrCallback; + callback = maybeCallback; + } else { + options = {}; + callback = optionsOrCallback; + } + } else { + url = undefined; + options = urlOrOptions || {}; + callback = optionsOrCallback; + } - if (typeof options === 'object') { + let fullUrl, headers, method, body; + if (url) { + fullUrl = url.toString(); + headers = { ...(options.headers || {}) }; + method = (options.method || 'GET').toUpperCase(); + body = options.body; + } else { const protocol = options.protocol || 'https:'; const hostname = options.hostname || 'localhost'; const port = options.port ? `:${options.port}` : ''; const path = options.path || '/'; - method = options.method || 'GET'; - - // Adiciona os query params, se existirem + body = options.body; const queryParams = options.query ? new URLSearchParams(options.query).toString() : ''; const queryString = queryParams ? `?${queryParams}` : ''; - fullUrl = `${protocol}//${hostname}${port}${path}${queryString}`; - headers = { ...options.headers }; - } else { - fullUrl = options; - headers = options.headers || {}; + headers = { ...(options.headers || {}) }; + method = (options.method || 'GET').toUpperCase(); } - const { body } = options; - - // Verifica o tipo de conteúdo e formata o corpo adequadamente let formattedBody; if (headers['Content-Type'] === 'application/json') { formattedBody = body ? JSON.stringify(body) : undefined; + if (!options.method && body) method = 'POST'; } else if (headers['Content-Type'] === 'application/x-www-form-urlencoded') { formattedBody = body ? new URLSearchParams(body).toString() : undefined; + if (!options.method && body) method = 'POST'; } else { - formattedBody = body; // Envia o corpo como está para outros tipos de conteúdo + formattedBody = body; + if (!options.method && body) method = 'POST'; } - const fetchOptions = { - method, - headers, - body: JSON.stringify(formattedBody) || JSON.stringify({}), - }; - const controller = new AbortController(); const signal = controller.signal; @@ -90,7 +98,7 @@ export const request = (options, callback) => { timeoutId = setTimeout(() => { controller.abort(); reqEvents.emit('error', new Error('Request timed out')); - }, 5000); + }, options.timeout); } const reqEvents = new EventEmitter(); @@ -99,9 +107,21 @@ export const request = (options, callback) => { const req = { _bodyBuffer: [], + removeHeader: (name) => { + if (headers && typeof name === 'string') { + delete headers[name]; + delete headers[name.toLowerCase()]; + } + }, + setHeader: (name, value) => { + if (headers && typeof name === 'string') { + headers[name] = value; + headers[name.toLowerCase()] = value; + } + }, write: (chunk) => { if (typeof chunk === 'string' || chunk instanceof Buffer) { - req._bodyBuffer.push(chunk); // Adiciona o chunk ao buffer + req._bodyBuffer.push(chunk); } else { throw new Error('Invalid chunk type. Expected string or Buffer.'); } @@ -125,37 +145,69 @@ export const request = (options, callback) => { } endCalled = true; - // Concatena os chunks do buffer para formar o corpo completo - const body = req._bodyBuffer.length > 0 ? req._bodyBuffer.join('') : undefined; + let bodyFinal; + if (req._bodyBuffer.length > 0) { + if (Buffer.isBuffer(req._bodyBuffer[0])) { + bodyFinal = Buffer.concat(req._bodyBuffer).toString(); + } else { + bodyFinal = req._bodyBuffer.join(''); + } + } else { + bodyFinal = formattedBody; + } + + const fetchOptions = { + method, + headers, + signal, + }; + + if (method !== 'GET' && method !== 'HEAD' && bodyFinal !== undefined) { + fetchOptions.body = bodyFinal; + } - fetch(fullUrl, { ...fetchOptions, body, signal }) + fetch(fullUrl, fetchOptions) .then(async (response) => { clearTimeout(timeoutId); - const res = new EventEmitter(); res.statusCode = response.status; res.headers = Object.fromEntries(response.headers.entries()); + const chunks = []; + res[Symbol.asyncIterator] = async function* () { + let ended = false; + this.on('end', () => { + ended = true; + }); + let idx = 0; + while (!ended || idx < chunks.length) { + if (idx < chunks.length) { + yield chunks[idx++]; + } else { + await new Promise((resolve) => this.once('data', resolve)); + } + } + }; + const reader = response.body?.getReader(); const processStream = async () => { try { if (!reader) { - console.log('No reader available'); - res.emit('end'); // Emite 'end' se não houver corpo na resposta + res.emit('end'); return; } - // eslint-disable-next-line no-constant-condition while (true) { const { done, value } = await reader.read(); if (done) { res.emit('end'); break; } - res.emit('data', Buffer.from(value)); + const buf = Buffer.from(value); + chunks.push(buf); + res.emit('data', buf); } } catch (err) { - console.error('Error processing stream:', err); res.emit('error', err); } }; @@ -172,7 +224,7 @@ export const request = (options, callback) => { reqEvents.emit('response', res); - if (callback) { + if (typeof callback === 'function') { callback(res); } }) diff --git a/packages/unenv-preset/src/polyfills/node/internal/_internal.js b/packages/unenv-preset/src/polyfills/node/internal/_internal.js new file mode 100644 index 00000000..81023975 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/_internal.js @@ -0,0 +1,11 @@ +/* eslint-disable */ +export function notImplemented(name) { + const fn = () => { + throw createNotImplementedError(name); + }; + return Object.assign(fn, { __unenv__: true }); +} + +export function createNotImplementedError(name) { + return new Error(`[unenv] ${name} is not implemented yet!`); +} diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/inherits.js b/packages/unenv-preset/src/polyfills/node/internal/util/inherits.js new file mode 100644 index 00000000..a8897e7d --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/inherits.js @@ -0,0 +1,15 @@ +/* eslint-disable */ +export function inherits(ctor, superCtor) { + if (!superCtor) { + return; + } + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true, + }, + }); +} diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/legacy-types.js b/packages/unenv-preset/src/polyfills/node/internal/util/legacy-types.js new file mode 100644 index 00000000..6e1a87c8 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/legacy-types.js @@ -0,0 +1,51 @@ +/* eslint-disable */ +export const isRegExp = (val) => val instanceof RegExp; + +export const isDate = (val) => val instanceof Date; + +export const isArray = (val) => Array.isArray(val); + +export const isBoolean = (val) => typeof val === 'boolean'; + +export const isNull = (val) => val === null; + +export const isNullOrUndefined = (val) => val === null || val === undefined; + +export const isNumber = (val) => typeof val === 'number'; + +export const isString = (val) => typeof val === 'string'; + +export const isSymbol = (val) => typeof val === 'symbol'; + +export const isUndefined = (val) => val === undefined; + +// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type +export const isFunction = (val) => typeof val === 'function'; + +export const isBuffer = (val) => { + return ( + val && + typeof val === 'object' && + typeof val.copy === 'function' && + typeof val.fill === 'function' && + typeof val.readUInt8 === 'function' + ); +}; + +export const isDeepStrictEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b); + +export const isObject = (val) => + val !== null && + typeof val === 'object' && + // eslint-disable-next-line no-prototype-builtins + Object.getPrototypeOf(val).isPrototypeOf(Object); + +export const isError = (val) => val instanceof Error; + +// Source https://github.com/jonschlinkert/is-primitive/blob/b22c524da5cbac075f14145780ec4b3637afd7dc/index.js +export const isPrimitive = (val) => { + if (typeof val === 'object') { + return val === null; + } + return typeof val !== 'function'; +}; diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/log.js b/packages/unenv-preset/src/polyfills/node/internal/util/log.js new file mode 100644 index 00000000..926cc546 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/log.js @@ -0,0 +1,64 @@ +/* eslint-disable */ +export const log = (...args) => { + console.log(...args); +}; + +export const debuglog = (section, _cb) => { + const fn = (msg, ...params) => { + if (fn.enabled) { + console.debug(`[${section}] ${msg}`, ...params); + } + }; + fn.enabled = true; + return fn; +}; + +export const debug = debuglog; + +export const inspect = (object) => JSON.stringify(object, null, 2); + +export const format = (...args) => _format(...args); + +export const formatWithOptions = (_options, ...args) => _format(...args); + +// Source: https://github.com/tmpfs/format-util/blob/0c989942c959b179eec294a4e725afd63e743f18/format.js +function _format(fmt, ...args) { + const re = /(%?)(%([djos]))/g; + if (args.length > 0) { + fmt = fmt.replace(re, (match, escaped, ptn, flag) => { + let arg = args.shift(); + switch (flag) { + case 'o': + if (Array.isArray(arg)) { + arg = JSON.stringify(arg); + break; + } + break; + case 's': + arg = '' + arg; + break; + case 'd': + arg = Number(arg); + break; + case 'j': + arg = JSON.stringify(arg); + break; + } + if (!escaped) { + return arg; + } + args.unshift(arg); + return match; + }); + } + + // arguments remain after formatting + if (args.length > 0) { + fmt += ' ' + args.join(' '); + } + + // update escaped %% values + fmt = fmt.replace(/%{2}/g, '%'); + + return '' + fmt; +} diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/mime.js b/packages/unenv-preset/src/polyfills/node/internal/util/mime.js new file mode 100644 index 00000000..9a7097d3 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/mime.js @@ -0,0 +1,45 @@ +/* eslint-disable */ +// https://nodejs.org/api/util.html#class-utilmimetype + +export class MIMEType { + __unenv__ = true; + + params = new MIMEParams(); + type; + subtype; + + constructor(input) { + const [essence = '', ...params] = String(input).split(';'); + const [type = '', subtype = ''] = essence.split('/'); + this.type = type; + this.subtype = subtype; + this.params = new MIMEParams(); + for (const param of params) { + const [name, value] = param.split('='); + this.params.set(name, value); + } + } + + get essence() { + return this.type + '/' + this.subtype; + } + + toString() { + const paramsStr = this.params.toString(); + return this.essence + (paramsStr ? `;${paramsStr}` : ''); + } +} + +// https://nodejs.org/api/util.html#util_class_util_mimeparams + +export class MIMEParams extends Map { + __unenv__ = true; + + get(name) { + return super.get(name) || null; + } + + toString() { + return [...this.entries()].map(([name, value]) => `${name}=${value}`).join('&'); + } +} diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/promisify.js b/packages/unenv-preset/src/polyfills/node/internal/util/promisify.js new file mode 100644 index 00000000..e5091ed2 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/promisify.js @@ -0,0 +1,25 @@ +/* eslint-disable */ +const customSymbol = Symbol.for('nodejs.util.promisify.custom'); + +function _promisify(fn) { + if (fn[customSymbol]) { + return fn[customSymbol]; + } + return function (...args) { + return new Promise((resolve, reject) => { + try { + fn.call(this, ...args, (err, val) => { + if (err) { + return reject(err); + } + resolve(val); + }); + } catch (error) { + console.error('Error in promisified function:', error.stack); + reject(error); + } + }); + }; +} + +export const promisify = Object.assign(_promisify, { custom: customSymbol }); diff --git a/packages/unenv-preset/src/polyfills/node/internal/util/types.js b/packages/unenv-preset/src/polyfills/node/internal/util/types.js new file mode 100644 index 00000000..09090c11 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/internal/util/types.js @@ -0,0 +1,131 @@ +/* eslint-disable */ +import { notImplemented } from '../_internal.js'; + +export const isExternal = (_obj) => false; + +export const isDate = (val) => val instanceof Date; + +export const isArgumentsObject = /*@__PURE__*/ notImplemented('util.types.isArgumentsObject'); + +export const isBigIntObject = (val) => val instanceof BigInt; + +export const isBooleanObject = (val) => val instanceof Boolean; + +export const isNumberObject = (val) => val instanceof Number; + +export const isStringObject = (val) => val instanceof String; + +export const isSymbolObject = (val) => val instanceof Symbol; + +export const isNativeError = /*@__PURE__*/ notImplemented('util.types.isNativeError'); + +export const isRegExp = (val) => val instanceof RegExp; + +export const isAsyncFunction = /*@__PURE__*/ notImplemented('util.types.isAsyncFunction'); + +export const isGeneratorFunction = /*@__PURE__*/ notImplemented('util.types.isGeneratorFunction'); + +export const isGeneratorObject = /*@__PURE__*/ notImplemented('util.types.isGeneratorObject'); + +export const isPromise = (val) => val instanceof Promise; + +export const isMap = (val) => val instanceof Map; + +export const isSet = (val) => val instanceof Set; + +export const isMapIterator = /*@__PURE__*/ notImplemented('util.types.isMapIterator'); + +export const isSetIterator = /*@__PURE__*/ notImplemented('util.types.isSetIterator'); + +export const isWeakMap = (val) => val instanceof WeakMap; + +export const isWeakSet = (val) => val instanceof WeakSet; + +export const isArrayBuffer = (val) => val instanceof ArrayBuffer; + +export const isDataView = (val) => val instanceof DataView; + +export const isSharedArrayBuffer = (val) => val instanceof SharedArrayBuffer; + +export const isProxy = /*@__PURE__*/ notImplemented('util.types.isProxy'); + +export const isModuleNamespaceObject = /*@__PURE__*/ notImplemented('util.types.isModuleNamespaceObject'); + +export const isAnyArrayBuffer = /*@__PURE__*/ notImplemented('util.types.isAnyArrayBuffer'); + +export const isBoxedPrimitive = /*@__PURE__*/ notImplemented('util.types.isBoxedPrimitive'); + +export const isArrayBufferView = /*@__PURE__*/ notImplemented('util.types.isArrayBufferView'); + +export const isTypedArray = /*@__PURE__*/ notImplemented('util.types.isTypedArray'); + +export const isUint8Array = /*@__PURE__*/ notImplemented('util.types.isUint8Array'); + +export const isUint8ClampedArray = /*@__PURE__*/ notImplemented('util.types.isUint8ClampedArray'); + +export const isUint16Array = /*@__PURE__*/ notImplemented('util.types.isUint16Array'); + +export const isUint32Array = /*@__PURE__*/ notImplemented('util.types.isUint32Array'); + +export const isInt8Array = /*@__PURE__*/ notImplemented('util.types.isInt8Array'); + +export const isInt16Array = /*@__PURE__*/ notImplemented('util.types.isInt16Array'); + +export const isInt32Array = /*@__PURE__*/ notImplemented('util.types.isInt32Array'); + +export const isFloat32Array = /*@__PURE__*/ notImplemented('util.types.isFloat32Array'); + +export const isFloat64Array = /*@__PURE__*/ notImplemented('util.types.isFloat64Array'); + +export const isBigInt64Array = /*@__PURE__*/ notImplemented('util.types.isBigInt64Array'); + +export const isBigUint64Array = /*@__PURE__*/ notImplemented('util.types.isBigUint64Array'); + +export const isKeyObject = /*@__PURE__*/ notImplemented('util.types.isKeyObject'); + +// export const isCryptoKey = /*@__PURE__*/ notImplemented('util.types.isCryptoKey'); +export const isCryptoKey = (val) => typeof CryptoKey !== 'undefined' && val instanceof CryptoKey; + +export default { + isExternal, + isDate, + isArgumentsObject, + isBigIntObject, + isBooleanObject, + isNumberObject, + isStringObject, + isSymbolObject, + isNativeError, + isRegExp, + isAsyncFunction, + isGeneratorFunction, + isGeneratorObject, + isPromise, + isMap, + isSet, + isMapIterator, + isSetIterator, + isWeakMap, + isWeakSet, + isArrayBuffer, + isDataView, + isSharedArrayBuffer, + isProxy, + isModuleNamespaceObject, + isAnyArrayBuffer, + isBoxedPrimitive, + isArrayBufferView, + isTypedArray, + isUint8Array, + isUint8ClampedArray, + isUint16Array, + isUint32Array, + isInt8Array, + isInt16Array, + isInt32Array, + isFloat32Array, + isFloat64Array, + isBigInt64Array, + isBigUint64Array, + isKeyObject, // CryptoKey +}; diff --git a/packages/unenv-preset/src/polyfills/node/util.js b/packages/unenv-preset/src/polyfills/node/util.js new file mode 100644 index 00000000..00df1acb --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/util.js @@ -0,0 +1,130 @@ +/* eslint-disable */ +// https://nodejs.org/api/util.html +import types from 'node:util/types'; +import { notImplemented } from './internal/_internal.js'; +import { inherits } from './internal/util/inherits.js'; +import { + isArray, + isBoolean, + isBuffer, + isDate, + isDeepStrictEqual, + isError, + isFunction, + isNull, + isNullOrUndefined, + isNumber, + isObject, + isPrimitive, + isRegExp, + isString, + isSymbol, + isUndefined, +} from './internal/util/legacy-types.js'; +import { debug, debuglog, format, formatWithOptions, inspect, log } from './internal/util/log.js'; +import { MIMEParams, MIMEType } from './internal/util/mime.js'; +import { promisify } from './internal/util/promisify.js'; + +export { MIMEParams, MIMEType } from './internal/util/mime.js'; + +export * from './internal/util/legacy-types.js'; + +export * from './internal/util/log.js'; + +export { inherits } from './internal/util/inherits.js'; + +export { promisify }; + +export { default as types } from './internal/util/types.js'; + +export const TextDecoder = globalThis.TextDecoder; + +export const TextEncoder = globalThis.TextEncoder; + +export const deprecate = (fn) => fn; + +export const _errnoException = notImplemented('util._errnoException'); + +export const _exceptionWithHostPort = notImplemented('util._exceptionWithHostPort'); + +export const _extend = notImplemented('util._extend'); + +export const aborted = notImplemented('util.aborted'); + +export const callbackify = notImplemented('util.callbackify'); + +export const getSystemErrorMap = notImplemented('util.getSystemErrorMap'); + +export const getSystemErrorName = notImplemented('util.getSystemErrorName'); + +export const toUSVString = notImplemented('util.toUSVString'); + +export const stripVTControlCharacters = notImplemented('util.stripVTControlCharacters'); + +export const transferableAbortController = notImplemented('util.transferableAbortController'); + +export const transferableAbortSignal = notImplemented('util.transferableAbortSignal'); + +export const parseArgs = notImplemented('util.parseArgs'); + +export const parseEnv = notImplemented('util.parseEnv'); + +export const styleText = notImplemented('util.styleText'); + +/** @deprecated */ +export const getCallSite = notImplemented('util.getCallSite'); + +export const getCallSites = notImplemented('util.getCallSites'); + +export const getSystemErrorMessage = notImplemented('util.getSystemErrorMessage'); + +export default { + // @ts-expect-error + _errnoException, + _exceptionWithHostPort, + _extend, + aborted, + callbackify, + deprecate, + getCallSite, + getCallSites, + getSystemErrorMessage, + getSystemErrorMap, + getSystemErrorName, + inherits, + promisify, + stripVTControlCharacters, + toUSVString, + TextDecoder, + TextEncoder, + types, + transferableAbortController, + transferableAbortSignal, + parseArgs, + parseEnv, + styleText, + MIMEParams, + MIMEType, + isArray, + isBoolean, + isBuffer, + isDate, + isDeepStrictEqual, + isError, + isFunction, + isNull, + isNullOrUndefined, + isNumber, + isObject, + isPrimitive, + isRegExp, + isString, + isSymbol, + isUndefined, + debug, + debuglog, + format, + formatWithOptions, + inspect, + log, +}; From 4b1e3de45544e2bd021aedbaee16df681d1cfa01 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 6 Jun 2025 18:05:56 +0000 Subject: [PATCH 18/34] chore(release): 1.20.0-stage.9 [skip ci] ## [1.20.0-stage.9](https://github.com/aziontech/lib/compare/v1.20.0-stage.8...v1.20.0-stage.9) (2025-06-06) ### Bug Fixes * polyfills for Node.js compatibility (#170) ([329f4c4](https://github.com/aziontech/lib/commit/329f4c41af43f8bf9ccede25a14605a8f34bdc35)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf888159..0cf722d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.9](https://github.com/aziontech/lib/compare/v1.20.0-stage.8...v1.20.0-stage.9) (2025-06-06) + + +### Bug Fixes + +* polyfills for Node.js compatibility (#170) ([329f4c4](https://github.com/aziontech/lib/commit/329f4c41af43f8bf9ccede25a14605a8f34bdc35)) + ## [1.20.0-stage.8](https://github.com/aziontech/lib/compare/v1.20.0-stage.7...v1.20.0-stage.8) (2025-06-03) diff --git a/package.json b/package.json index bbdbf85d..7c8bb3a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.8", + "version": "1.20.0-stage.9", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From ea99874afcab25e0144b374938f704b2e9205987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Mon, 9 Jun 2025 10:46:49 -0300 Subject: [PATCH 19/34] fix: add polyfill for clearInterval and update injection in unenv preset (#172) --- packages/unenv-preset/src/index.ts | 2 +- .../polyfills/node/globals/clear-interval.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 packages/unenv-preset/src/polyfills/node/globals/clear-interval.js diff --git a/packages/unenv-preset/src/index.ts b/packages/unenv-preset/src/index.ts index 73b30fe0..45db6209 100644 --- a/packages/unenv-preset/src/index.ts +++ b/packages/unenv-preset/src/index.ts @@ -14,6 +14,7 @@ export default { process: `${polyfillsPath}/node/globals/process.cjs`, performance: `unenv/polyfill/performance`, setInterval: `${polyfillsPath}/node/globals/set-interval.js`, + clearInterval: `${polyfillsPath}/node/globals/clear-interval.js`, }, alias: { 'azion/utils': 'azion/utils', @@ -27,7 +28,6 @@ export default { string_decoder: 'string_decoder/lib/string_decoder.js', timers: 'timers-browserify/', util: `${polyfillsPath}/node/util.js`, - 'util/types': `${polyfillsPath}/node/internal/util/types.js`, }, external: ['node:async_hooks', 'node:fs/promises', 'node:stream', 'node:crypto'], polyfill: [ diff --git a/packages/unenv-preset/src/polyfills/node/globals/clear-interval.js b/packages/unenv-preset/src/polyfills/node/globals/clear-interval.js new file mode 100644 index 00000000..43f91f61 --- /dev/null +++ b/packages/unenv-preset/src/polyfills/node/globals/clear-interval.js @@ -0,0 +1,19 @@ +const _clearInterval = globalThis.clearInterval; + +globalThis.clearInterval = (interval) => { + let idToClear = interval; + if (interval && typeof interval === 'object') { + if ('id' in interval) { + idToClear = interval.id; + } else if ('_id' in interval) { + idToClear = interval._id; + } + } + if (typeof idToClear === 'number' && Number.isFinite(idToClear)) { + _clearInterval(idToClear); + } else if (interval && typeof interval.close === 'function') { + interval.close(); + } +}; + +export default globalThis.clearInterval; From 1ac3f824a778595e99a2e345f89aa59428d02d84 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 9 Jun 2025 13:48:06 +0000 Subject: [PATCH 20/34] chore(release): 1.20.0-stage.10 [skip ci] ## [1.20.0-stage.10](https://github.com/aziontech/lib/compare/v1.20.0-stage.9...v1.20.0-stage.10) (2025-06-09) ### Bug Fixes * add polyfill for clearInterval and update injection in unenv preset (#172) ([ea99874](https://github.com/aziontech/lib/commit/ea99874afcab25e0144b374938f704b2e9205987)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cf722d8..0e3efc45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.10](https://github.com/aziontech/lib/compare/v1.20.0-stage.9...v1.20.0-stage.10) (2025-06-09) + + +### Bug Fixes + +* add polyfill for clearInterval and update injection in unenv preset (#172) ([ea99874](https://github.com/aziontech/lib/commit/ea99874afcab25e0144b374938f704b2e9205987)) + ## [1.20.0-stage.9](https://github.com/aziontech/lib/compare/v1.20.0-stage.8...v1.20.0-stage.9) (2025-06-06) diff --git a/package.json b/package.json index 7c8bb3a0..5d75d577 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.9", + "version": "1.20.0-stage.10", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 86747855fc8f7297d9a937a3c1eff190eb8a5def Mon Sep 17 00:00:00 2001 From: Pablo Diehl Date: Tue, 17 Jun 2025 14:04:06 -0300 Subject: [PATCH 21/34] fix(presets): add missing prebuild command in preact preset --- packages/presets/src/presets/preact/index.ts | 5 +- .../presets/src/presets/preact/prebuild.ts | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 packages/presets/src/presets/preact/prebuild.ts diff --git a/packages/presets/src/presets/preact/index.ts b/packages/presets/src/presets/preact/index.ts index 6766e028..38b0132d 100644 --- a/packages/presets/src/presets/preact/index.ts +++ b/packages/presets/src/presets/preact/index.ts @@ -2,7 +2,6 @@ import type { AzionBuildPreset } from 'azion/config'; import config from './config'; import handler from './handler'; import metadata from './metadata'; -// import prebuild from './prebuild'; -// import postbuild from './postbuild'; +import prebuild from './prebuild'; -export const preact: AzionBuildPreset = { config, metadata, handler }; +export const preact: AzionBuildPreset = { config, metadata, handler, prebuild }; diff --git a/packages/presets/src/presets/preact/prebuild.ts b/packages/presets/src/presets/preact/prebuild.ts new file mode 100644 index 00000000..1426fb16 --- /dev/null +++ b/packages/presets/src/presets/preact/prebuild.ts @@ -0,0 +1,87 @@ +import { copyDirectory, exec, getPackageManager } from 'azion/utils/node'; +import { lstat, readFile, rm } from 'fs/promises'; +import { join } from 'path'; + +const packageManager = await getPackageManager(); +const edgeStorageDir = '.edge/storage'; +const defaultViteOutDir = 'dist'; + +/** + * Check if the vite.config file exists. + * @returns {boolean} True if the file exists, false otherwise. + */ +async function viteConfigExists() { + const files = ['./vite.config.js', './vite.config.ts']; + const checks = files.map(async (file) => { + try { + await lstat(file); + return true; + } catch (err) { + return false; + } + }); + + const results = await Promise.all(checks); + return results.includes(true); +} + +/** + * Read vite.config build output + * @returns {object} The parsed configuration object or null if the file doesn't exist. + */ +async function readViteBuildOutput() { + try { + const isTypescript = await lstat('./vite.config.ts').then( + () => true, + () => false, + ); + const pathConfigFile = join(process.cwd(), isTypescript ? 'vite.config.ts' : 'vite.config.js'); + const configFileContent = await readFile(pathConfigFile, 'utf-8'); + const defineConfig = configFileContent.match(/defineConfig\(([\s\S]*)\)/); + + if (!defineConfig) { + throw new Error('defineConfig not found'); + } + + const buildConfig = defineConfig[1].match(/build: *({[\s\S]*?}),/); + + if (!buildConfig) { + throw new Error('build config not found'); + } + + const buildConfigObject = JSON.parse(buildConfig[1].replace(/(\w+):/g, '"$1":').replace(/'/g, '"')); + + return Promise.resolve({ build: buildConfigObject }); + } catch (err) { + return null; + } +} + +/** + * Runs custom prebuild actions + */ +async function prebuild() { + const npmArgsForward = packageManager === 'npm' ? '--' : ''; + + let outDir = defaultViteOutDir; + const destPath = edgeStorageDir; + + const isViteProject = await viteConfigExists(); + + if (isViteProject) { + await exec(`${packageManager} run build ${npmArgsForward}`, 'Preact', true); + + const config = await readViteBuildOutput(); + + if (config?.build?.outDir) { + outDir = config.build.outDir; + } + + copyDirectory(outDir, destPath); + rm(outDir, { recursive: true, force: true }); + } else { + await exec(`BUILD_PATH="./.edge/storage" ${packageManager} run build`, 'Preact', true); + } +} + +export default prebuild; From 4fce73d05975839a879018397c2f263b745d631f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 18 Jun 2025 11:08:07 +0000 Subject: [PATCH 22/34] chore(release): 1.20.0-stage.11 [skip ci] ## [1.20.0-stage.11](https://github.com/aziontech/lib/compare/v1.20.0-stage.10...v1.20.0-stage.11) (2025-06-18) ### Bug Fixes * **presets:** add missing prebuild command in preact preset ([8674785](https://github.com/aziontech/lib/commit/86747855fc8f7297d9a937a3c1eff190eb8a5def)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e3efc45..e4e5614c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.11](https://github.com/aziontech/lib/compare/v1.20.0-stage.10...v1.20.0-stage.11) (2025-06-18) + + +### Bug Fixes + +* **presets:** add missing prebuild command in preact preset ([8674785](https://github.com/aziontech/lib/commit/86747855fc8f7297d9a937a3c1eff190eb8a5def)) + ## [1.20.0-stage.10](https://github.com/aziontech/lib/compare/v1.20.0-stage.9...v1.20.0-stage.10) (2025-06-09) diff --git a/package.json b/package.json index 5d75d577..76de4248 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.10", + "version": "1.20.0-stage.11", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 90b0def35c64370cc67c523f5ef95bb2979663b6 Mon Sep 17 00:00:00 2001 From: Pablo Diehl Date: Wed, 18 Jun 2025 13:22:35 -0300 Subject: [PATCH 23/34] fix(preset): update ruleset of html preset --- packages/presets/src/presets/html/config.ts | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/presets/src/presets/html/config.ts b/packages/presets/src/presets/html/config.ts index 00f79162..8088c189 100644 --- a/packages/presets/src/presets/html/config.ts +++ b/packages/presets/src/presets/html/config.ts @@ -24,6 +24,31 @@ export default defineConfig({ }, }, }, + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, + }, + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, ], }, }); From e93e07306609e3df88ffce7e4a56cf2f414f2640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Wed, 18 Jun 2025 16:24:42 -0300 Subject: [PATCH 24/34] revert: api v4 (#176) * Revert "fix: api v4 (#168)" This reverts commit dc33e5a9cfe9bd069d5bb5fd61fe026ddde53dcb. * Revert "feat: purge api v4 (#167)" This reverts commit f779d6af4f2c9fd03fdce45d851f72347c21f3b9. * Revert "feat(config): azion api v4 (#161)" --- package-lock.json | 4 +- package.json | 10 - .../src/rules-engine/services/types.ts | 4 +- packages/client/src/types.ts | 2 +- packages/config/README.md | 6 +- packages/config/package.json | 14 +- .../converterJsonConfig/index.test.ts | 126 +- .../helpers/azion.config.example.ts | 10 +- .../src/configProcessor/helpers/behaviors.ts | 87 +- .../helpers/convertLegacyConfig.ts | 68 + .../src/configProcessor/helpers/schema.ts | 1468 ++++++----------- .../configProcessor/helpers/schemaManifest.ts | 904 +++------- .../processConfig/index.test.ts | 110 +- .../configProcessor/processConfig/index.ts | 4 +- .../edgeApplicationProcessConfigStrategy.ts | 73 - .../cacheProcessConfigStrategy.ts | 54 +- .../domainProcessConfigStrategy.ts | 82 + .../edgeConnectorProcessConfigStrategy.ts | 248 --- .../functionsProcessConfigStrategy.ts | 73 +- .../localProcessConfigStrategy.ts | 33 + .../originProcessConfigStrategy.ts | 126 ++ .../purgeProcessConfigStrategy.ts | 22 +- .../rulesProcessConfigStrategy.ts | 113 +- .../firewallProcessConfigStrategy.test.ts | 413 +++-- .../secure/firewallProcessConfigStrategy.ts | 174 +- .../storageProcessConfigStrategy.ts | 39 - .../workloadProcessConfigStrategy.ts | 106 -- .../configProcessor/processStrategy/index.ts | 29 +- .../processStrategy/processConfigContext.ts | 2 +- .../processStrategy/processConfigStrategy.ts | 4 +- .../validateConfig/index.test.ts | 3 + packages/config/src/constants.ts | 39 +- packages/config/src/rules/constants.ts | 38 - packages/config/src/rules/index.ts | 2 - .../config/src/rules/request/createMPA.ts | 55 - .../config/src/rules/request/createSPA.ts | 47 - packages/config/src/types.ts | 311 ++-- packages/config/tsconfig.json | 3 +- packages/config/tsup.config.ts | 13 - .../presets/src/presets/angular/config.ts | 66 +- packages/presets/src/presets/astro/config.ts | 72 +- .../presets/src/presets/docusaurus/config.ts | 72 +- .../presets/src/presets/eleventy/config.ts | 72 +- .../presets/src/presets/emscripten/config.ts | 41 +- packages/presets/src/presets/gatsby/config.ts | 74 +- packages/presets/src/presets/hexo/config.ts | 74 +- packages/presets/src/presets/html/config.ts | 51 +- packages/presets/src/presets/hugo/config.ts | 74 +- .../presets/src/presets/javascript/config.ts | 44 +- packages/presets/src/presets/jekyll/config.ts | 74 +- packages/presets/src/presets/next/config.ts | 97 +- packages/presets/src/presets/nuxt/config.ts | 76 +- packages/presets/src/presets/preact/config.ts | 68 +- packages/presets/src/presets/qwik/config.ts | 67 +- packages/presets/src/presets/react/config.ts | 67 +- .../presets/src/presets/rustwasm/config.ts | 43 +- .../presets/src/presets/stencil/config.ts | 67 +- packages/presets/src/presets/svelte/config.ts | 76 +- .../presets/src/presets/typescript/config.ts | 42 +- .../presets/src/presets/vitepress/config.ts | 74 +- packages/presets/src/presets/vue/config.ts | 66 +- .../presets/src/presets/vuepress/config.ts | 66 +- packages/presets/tsconfig.json | 1 - 63 files changed, 2760 insertions(+), 3683 deletions(-) create mode 100644 packages/config/src/configProcessor/helpers/convertLegacyConfig.ts delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts rename packages/config/src/configProcessor/processStrategy/implementations/{application => }/cacheProcessConfigStrategy.ts (67%) create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts rename packages/config/src/configProcessor/processStrategy/implementations/{application => }/rulesProcessConfigStrategy.ts (69%) delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts delete mode 100644 packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts delete mode 100644 packages/config/src/rules/constants.ts delete mode 100644 packages/config/src/rules/index.ts delete mode 100644 packages/config/src/rules/request/createMPA.ts delete mode 100644 packages/config/src/rules/request/createSPA.ts delete mode 100644 packages/config/tsup.config.ts diff --git a/package-lock.json b/package-lock.json index ff4a6533..84963ea7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "azion", - "version": "1.20.0-stage.1", + "version": "1.19.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "azion", - "version": "1.20.0-stage.1", + "version": "1.19.1", "license": "MIT", "workspaces": [ "packages/*" diff --git a/package.json b/package.json index 76de4248..9b9a27ed 100644 --- a/package.json +++ b/package.json @@ -115,10 +115,6 @@ "require": "./packages/config/dist/index.js", "import": "./packages/config/dist/index.mjs" }, - "./config/rules": { - "require": "./packages/config/dist/rules/index.js", - "import": "./packages/config/dist/rules/index.mjs" - }, "./sql": { "require": "./packages/sql/dist/index.js", "import": "./packages/sql/dist/index.mjs" @@ -169,12 +165,6 @@ "ai": [ "./packages/ai/dist/index.d.ts" ], - "config": [ - "./packages/config/dist/index.d.ts" - ], - "config/rules": [ - "./packages/config/dist/rules/index.d.ts" - ], "presets": [ "./packages/presets/dist/index.d.ts" ], diff --git a/packages/applications/src/rules-engine/services/types.ts b/packages/applications/src/rules-engine/services/types.ts index c5c78b60..99ce5d54 100644 --- a/packages/applications/src/rules-engine/services/types.ts +++ b/packages/applications/src/rules-engine/services/types.ts @@ -43,7 +43,7 @@ export interface Criterion { variable: string; operator: string; conditional: 'if' | 'and' | 'or'; - argument: string; + input_value: string; } export interface ApiCreateRulePayload { @@ -107,7 +107,7 @@ export interface Criterion { variable: string; operator: string; conditional: 'if' | 'and' | 'or'; - argument: string; + input_value: string; } export interface ApiCreateRulePayload { diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 1491285b..d085dae4 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -172,7 +172,7 @@ export interface AzionClient { * const { data: newRule } = await app.rules.request.createRule({ * data: { * name: 'My Rule', - * behaviors: [{ name: 'set_origin', argument: newOrigin.id }], + * behaviors: [{ name: 'set_origin', target: newOrigin.id }], * criteria: [{ condition: 'starts_with', variable: '${uri}', input: '/api' }] * } * }); diff --git a/packages/config/README.md b/packages/config/README.md index 60e88df5..49213a8b 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -129,7 +129,7 @@ const config = defineConfig({ variable: 'remote_addr', operator: 'in', conditional: 'if', - argument: 'suspicious_ips', + inputValue: 'suspicious_ips', }, ], behavior: { @@ -145,7 +145,7 @@ const config = defineConfig({ variable: 'uri', operator: 'starts_with', conditional: 'if', - argument: '/api/', + inputValue: '/api/', }, ], behavior: { @@ -597,7 +597,7 @@ Type definition for the response rule configuration. - `variable: RuleVariable` - Variable to be evaluated. - `conditional: RuleConditional` - Conditional type. - `operator: RuleOperatorWithValue | RuleOperatorWithoutValue` - Comparison operator. - - `argument?: string` - Input value for comparison (required for operators with value). + - `inputValue?: string` - Input value for comparison (required for operators with value). ### `AzionWaf` diff --git a/packages/config/package.json b/packages/config/package.json index c4fe9076..40cd0b8b 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -6,7 +6,7 @@ "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "scripts": { - "compile": "tsup --config ./tsup.config.ts", + "compile": "tsup --config ../../tsup.config.json", "lint": "eslint .", "lint:fix": "eslint --fix .", "prettier": "prettier --write .", @@ -14,18 +14,6 @@ "test:watch": "jest -c jest.config.js . --watch", "test:coverage": "jest --clearCache && jest -c jest.config.js . --coverage" }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "require": "./dist/index.cjs", - "import": "./dist/index.js" - }, - "./rules": { - "types": "./dist/rules/index.d.ts", - "require": "./dist/rules/index.cjs", - "import": "./dist/rules/index.js" - } - }, "author": "aziontech", "license": "MIT", "files": [ diff --git a/packages/config/src/configProcessor/converterJsonConfig/index.test.ts b/packages/config/src/configProcessor/converterJsonConfig/index.test.ts index 70d02d23..b3687906 100644 --- a/packages/config/src/configProcessor/converterJsonConfig/index.test.ts +++ b/packages/config/src/configProcessor/converterJsonConfig/index.test.ts @@ -201,7 +201,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -227,7 +227,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], behavior: { @@ -250,7 +250,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -282,7 +282,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule responds with a file from the origin storage.', @@ -309,7 +309,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -333,7 +333,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule rewrites the request path.', @@ -357,7 +357,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -380,7 +380,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule delivers the request.', @@ -404,7 +404,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -428,7 +428,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule sets a cookie.', @@ -452,7 +452,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -484,7 +484,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule sets multiple headers.', @@ -508,7 +508,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -538,7 +538,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule sets the cache.', @@ -562,7 +562,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -596,7 +596,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule sets the cache.', @@ -624,7 +624,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -648,7 +648,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule forwards the cookie.', @@ -679,7 +679,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -703,7 +703,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule runs a function.', @@ -729,7 +729,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -752,7 +752,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule enables GZIP compression.', @@ -778,7 +778,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -801,7 +801,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule bypasses the cache.', @@ -827,7 +827,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${scheme}`, operator: 'matches', conditional: 'if', - argument: 'http', + input_value: 'http', }, ], ], @@ -852,7 +852,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${scheme}`, operator: 'matches', conditional: 'if', - argument: 'http', + inputValue: 'http', }, ], description: 'This rule redirects HTTP to HTTPS.', @@ -877,7 +877,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -905,7 +905,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule captures the request.', @@ -935,7 +935,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -963,7 +963,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule captures the request.', @@ -990,7 +990,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1014,7 +1014,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule the request.', @@ -1040,7 +1040,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/images', + input_value: '/images', }, ], ], @@ -1064,7 +1064,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/images', + inputValue: '/images', }, ], description: 'This rule the request.', @@ -1089,7 +1089,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1121,7 +1121,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule sets multiple headers.', @@ -1146,7 +1146,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/login', + input_value: '/login', }, ], ], @@ -1170,7 +1170,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/login', + inputValue: '/login', }, ], description: 'This rule the request.', @@ -1198,7 +1198,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1223,7 +1223,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], active: false, @@ -1248,7 +1248,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1274,7 +1274,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], behavior: { @@ -1298,7 +1298,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + input_value: '200', }, ], ], @@ -1322,7 +1322,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + inputValue: '200', }, ], active: false, @@ -1347,7 +1347,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + input_value: '200', }, ], ], @@ -1371,7 +1371,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + inputValue: '200', }, ], description: 'This rule sets a header.', @@ -1395,7 +1395,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + input_value: '200', }, ], ], @@ -1418,7 +1418,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + inputValue: '200', }, ], description: 'This rule enables GZIP compression.', @@ -1443,7 +1443,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1468,7 +1468,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule filters the cookie.', @@ -1493,7 +1493,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + input_value: '200', }, ], ], @@ -1518,7 +1518,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${status}`, operator: 'equals', conditional: 'if', - argument: '200', + inputValue: '200', }, ], description: 'This rule filters the header.', @@ -1549,7 +1549,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -1573,7 +1573,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'matches', conditional: 'if', - argument: '/test', + inputValue: '/test', }, ], description: 'This rule runs a function.', @@ -1599,7 +1599,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'equals', conditional: 'if', - argument: '/', + input_value: '/', }, ], ], @@ -1624,7 +1624,7 @@ describe('convertJsonConfigToObject', () => { variable: `\${uri}`, operator: 'equals', conditional: 'if', - argument: '/', + inputValue: '/', }, ], description: 'This rule delivers the response.', @@ -1909,7 +1909,7 @@ describe('convertJsonConfigToObject', () => { variable: 'invalid_variable', operator: 'is_equal', conditional: 'if', - argument: 'test', + input_value: 'test', }, }, ], @@ -1935,7 +1935,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'is_equal', conditional: 'if', - argument: '/test', + input_value: '/test', }, behavior: { name: 'invalid_behavior', @@ -1965,7 +1965,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'is_equal', conditional: 'if', - argument: '/test', + input_value: '/test', }, behavior: { name: 'set_rate_limit', @@ -2132,7 +2132,7 @@ describe('convertJsonConfigToObject', () => { variable: 'invalid_variable', operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -2169,7 +2169,7 @@ describe('convertJsonConfigToObject', () => { variable: 'request_uri', operator: 'matches', conditional: 'if', - argument: '/test', + input_value: '/test', }, ], ], @@ -2190,7 +2190,7 @@ describe('convertJsonConfigToObject', () => { ); }); - it('should throw an error when argument is missing for operator that requires it', () => { + it('should throw an error when input_value is missing for operator that requires it', () => { const jsonConfig = { application: [ { @@ -2222,7 +2222,7 @@ describe('convertJsonConfigToObject', () => { }; expect(() => convertJsonConfigToObject(JSON.stringify(jsonConfig))).toThrow( - "The operator 'matches' requires an argument.", + "The operator 'matches' requires an input_value.", ); }); }); diff --git a/packages/config/src/configProcessor/helpers/azion.config.example.ts b/packages/config/src/configProcessor/helpers/azion.config.example.ts index 25a0bc5f..2f890e76 100644 --- a/packages/config/src/configProcessor/helpers/azion.config.example.ts +++ b/packages/config/src/configProcessor/helpers/azion.config.example.ts @@ -241,7 +241,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -258,7 +258,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -274,7 +274,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/login', + inputValue: '^/login', }, ], behavior: { @@ -356,7 +356,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -372,7 +372,7 @@ export default { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { diff --git a/packages/config/src/configProcessor/helpers/behaviors.ts b/packages/config/src/configProcessor/helpers/behaviors.ts index d7ef4125..aa275406 100644 --- a/packages/config/src/configProcessor/helpers/behaviors.ts +++ b/packages/config/src/configProcessor/helpers/behaviors.ts @@ -1,16 +1,18 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export const requestBehaviors = { - setEdgeConnector: { + setOrigin: { transform: (value: any, payloadCDN: any) => { - const connectorName = typeof value === 'string' ? value : value.name; - const connector = payloadCDN.edgeConnectors?.find((o: any) => o.name === connectorName); - if (!connector) { - throw new Error(`Rule setEdgeConnector '${connectorName}' not found in the edge connectors list`); + const origin = payloadCDN.origin?.find((o: any) => o.name === value.name && o.origin_type === value.type); + + if (!origin) { + throw new Error(`Rule setOrigin name '${value.name}' not found in the origin settings`); + } else if (origin.origin_type !== value.type) { + throw new Error(`Rule setOrigin originType '${value.type}' does not match the origin settings`); } return { - name: 'set_edge_connector', - argument: connector.name, + name: 'set_origin', + target: origin.name, }; }, }, @@ -19,7 +21,7 @@ export const requestBehaviors = { const behaviors = []; behaviors.push({ name: 'rewrite_request', - argument: value, + target: value, }); return behaviors; }, @@ -27,20 +29,20 @@ export const requestBehaviors = { deliver: { transform: () => ({ name: 'deliver', - argument: null, + target: null, }), }, setCookie: { transform: (value: any) => ({ name: 'add_request_cookie', - argument: value, + target: value, }), }, setHeaders: { transform: (value: any) => value.map((header: any) => ({ name: 'add_request_header', - argument: header, + target: header, })), }, setCache: { @@ -48,7 +50,7 @@ export const requestBehaviors = { if (typeof value === 'string') { return { name: 'set_cache_policy', - argument: value, + target: value, }; } if (typeof value === 'object') { @@ -60,7 +62,7 @@ export const requestBehaviors = { payloadCDN.cache.push(cacheSetting); return { name: 'set_cache_policy', - argument: value.name, + target: value.name, }; } return undefined; @@ -71,7 +73,7 @@ export const requestBehaviors = { if (value) { return { name: 'forward_cookies', - argument: null, + target: null, }; } return undefined; @@ -80,7 +82,7 @@ export const requestBehaviors = { runFunction: { transform: (value: string) => ({ name: 'run_function', - argument: value, + target: value, }), }, enableGZIP: { @@ -88,7 +90,7 @@ export const requestBehaviors = { if (value) { return { name: 'enable_gzip', - argument: '', + target: '', }; } return undefined; @@ -99,7 +101,7 @@ export const requestBehaviors = { if (value) { return { name: 'bypass_cache_phase', - argument: null, + target: null, }; } return undefined; @@ -110,7 +112,7 @@ export const requestBehaviors = { if (value) { return { name: 'redirect_http_to_https', - argument: null, + target: null, }; } return undefined; @@ -119,19 +121,19 @@ export const requestBehaviors = { redirectTo301: { transform: (value: any) => ({ name: 'redirect_to_301', - argument: value, + target: value, }), }, redirectTo302: { transform: (value: any) => ({ name: 'redirect_to_302', - argument: value, + target: value, }), }, capture: { transform: (value: any) => ({ name: 'capture_match_groups', - argument: { + target: { regex: value.match, captured_array: value.captured, subject: `\${${value.subject ?? 'uri'}}`, @@ -141,13 +143,13 @@ export const requestBehaviors = { filterCookie: { transform: (value: any) => ({ name: 'filter_request_cookie', - argument: value, + target: value, }), }, filterHeader: { transform: (value: any) => ({ name: 'filter_request_header', - argument: value, + target: value, }), }, noContent: { @@ -155,7 +157,7 @@ export const requestBehaviors = { if (value) { return { name: 'no_content', - argument: null, + target: null, }; } return undefined; @@ -166,7 +168,7 @@ export const requestBehaviors = { if (value) { return { name: 'optimize_images', - argument: null, + target: null, }; } return undefined; @@ -177,7 +179,7 @@ export const requestBehaviors = { if (value) { return { name: 'deny', - argument: null, + target: null, }; } return undefined; @@ -188,14 +190,14 @@ export const responseBehaviors = { setCookie: { transform: (value: any) => ({ name: 'set_cookie', - argument: value, + target: value, }), }, setHeaders: { transform: (value: any) => value.map((header: any) => ({ name: 'add_response_header', - argument: header, + target: header, })), }, enableGZIP: { @@ -203,7 +205,7 @@ export const responseBehaviors = { if (value) { return { name: 'enable_gzip', - argument: '', + target: '', }; } return undefined; @@ -212,37 +214,37 @@ export const responseBehaviors = { filterCookie: { transform: (value: any) => ({ name: 'filter_response_cookie', - argument: value, + target: value, }), }, filterHeader: { transform: (value: any) => ({ name: 'filter_response_header', - argument: value, + target: value, }), }, runFunction: { transform: (value: string) => ({ name: 'run_function', - argument: value, + target: value, }), }, redirectTo301: { transform: (value: any) => ({ name: 'redirect_to_301', - argument: value, + target: value, }), }, redirectTo302: { transform: (value: any) => ({ name: 'redirect_to_302', - argument: value, + target: value, }), }, capture: { transform: (value: any) => ({ name: 'capture_match_groups', - argument: { + target: { regex: value.match, captured_array: value.captured, subject: `\${${value.subject ?? 'uri'}}`, @@ -254,7 +256,7 @@ export const responseBehaviors = { if (value) { return { name: 'deliver', - argument: null, + target: null, }; } return undefined; @@ -263,14 +265,17 @@ export const responseBehaviors = { }; export const revertRequestBehaviors = { - set_edge_connector: { + set_origin: { transform: (value: any, payloadCDN: any) => { - const connector = payloadCDN.edgeConnectors?.find((o: any) => o.name === value); - if (!connector) { - throw new Error(`Rule setEdgeConnector name '${value.name}' not found in the edge connectors list`); + const origin = payloadCDN.origin?.find((o: any) => o.name === value); + if (!origin) { + throw new Error(`Rule setOrigin name '${value.name}' not found in the origin settings`); } return { - setEdgeConnector: value, + setOrigin: { + name: value, + type: origin.type, + }, }; }, }, diff --git a/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts b/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts new file mode 100644 index 00000000..6f8e44ad --- /dev/null +++ b/packages/config/src/configProcessor/helpers/convertLegacyConfig.ts @@ -0,0 +1,68 @@ +/** + * Converts legacy configuration to the new format by moving old properties into the `behavior` object. + * This ensures backward compatibility for projects that do not use the `behavior` field. + * @param {object} config - The configuration object to be converted. + * @returns The converted configuration object with `behavior` properties. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function convertLegacyConfig(config: any) { + const newConfig = { ...config }; + + // Define the properties that should be moved to the behavior object for request and response + const requestBehaviorProperties = [ + 'httpToHttps', + 'bypassCache', + 'forwardCookies', + 'capture', + 'setOrigin', + 'rewrite', + 'setCookie', + 'setHeaders', + 'runFunction', + 'setCache', + 'redirectTo301', + 'redirectTo302', + 'deliver', + ]; + + const responseBehaviorProperties = [ + 'enableGZIP', + 'capture', + 'setCookie', + 'setHeaders', + 'filterHeader', + 'filterCookie', + 'runFunction', + 'redirectTo301', + 'redirectTo302', + 'deliver', + ]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const convertRules = (rules: any, behaviorProperties: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return rules.map((rule: any) => { + const newRule = { ...rule, behavior: { ...rule.behavior } }; + + // Preserve the order of properties + Object.keys(rule).forEach((prop) => { + if (behaviorProperties.includes(prop)) { + newRule.behavior[prop] = rule[prop]; + delete newRule[prop]; + } + }); + + return newRule; + }); + }; + + if (newConfig.rules && newConfig.rules.request) { + newConfig.rules.request = convertRules(newConfig.rules.request, requestBehaviorProperties); + } + + if (newConfig.rules && newConfig.rules.response) { + newConfig.rules.response = convertRules(newConfig.rules.response, responseBehaviorProperties); + } + return newConfig; +} + +export default convertLegacyConfig; diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 90d7e5b8..4880d56c 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -2,9 +2,6 @@ import { ALL_REQUEST_VARIABLES, ALL_RESPONSE_VARIABLES, DYNAMIC_VARIABLE_PATTERNS, - EDGE_CONNECTOR_CONNECTION_PREFERENCE, - EDGE_CONNECTOR_LOAD_BALANCE, - EDGE_CONNECTOR_TYPES, FIREWALL_RATE_LIMIT_BY, FIREWALL_RATE_LIMIT_TYPES, FIREWALL_VARIABLES, @@ -17,11 +14,6 @@ import { SPECIAL_VARIABLES, WAF_MODE, WAF_SENSITIVITY, - WORKLOAD_HTTP_VERSIONS, - WORKLOAD_MTLS_VERIFICATION, - WORKLOAD_NETWORK_MAP, - WORKLOAD_TLS_CIPHERS, - WORKLOAD_TLS_VERSIONS, } from '../../constants'; const criteriaBaseSchema = { @@ -64,11 +56,11 @@ const criteriaBaseSchema = { }, }, then: { - required: ['argument'], + required: ['inputValue'], properties: { - argument: { + inputValue: { type: 'string', - errorMessage: "The 'argument' field must be a string", + errorMessage: "The 'inputValue' field must be a string", }, }, }, @@ -81,7 +73,7 @@ const criteriaBaseSchema = { }, then: { not: { - required: ['argument'], + required: ['inputValue'], }, }, }, @@ -164,9 +156,24 @@ const createRuleSchema = (isRequestPhase = false) => ({ behavior: { type: 'object', properties: { - setEdgeConnector: { - type: 'string', - errorMessage: "The 'setEdgeConnector' field must be a string.", + setOrigin: { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + type: { + type: 'string', + errorMessage: "The 'type' field must be a string.", + }, + }, + required: ['name', 'type'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'setOrigin' object.", + required: "The 'name or type' field is required in the 'setOrigin' object.", + }, }, rewrite: { type: 'string', @@ -252,10 +259,6 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['wafMode', 'wafId'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the setWafRuleset object', - required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset", - }, }, setRateLimit: { type: 'object', @@ -281,11 +284,6 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the setRateLimit object', - required: - "All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit", - }, }, deny: { type: 'boolean', @@ -315,10 +313,6 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, required: ['statusCode', 'contentType', 'contentBody'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the setCustomResponse object', - required: "All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse", - }, }, tagEvent: { type: 'object', @@ -344,15 +338,15 @@ const createRuleSchema = (isRequestPhase = false) => ({ type: 'string', errorMessage: "The 'name' field must be a string.", }, - browserCacheSettingsMaximumTtl: { + browser_cache_settings_maximum_ttl: { type: 'number', nullable: true, - errorMessage: "The 'browserCacheSettingsMaximumTtl' field must be a number or null.", + errorMessage: "The 'browser_cache_settings_maximum_ttl' field must be a number or null.", }, - cdnCacheSettingsMaximumTtl: { + cdn_cache_settings_maximum_ttl: { type: 'number', nullable: true, - errorMessage: "The 'cdnCacheSettingsMaximumTtl' field must be a number or null.", + errorMessage: "The 'cdn_cache_settings_maximum_ttl' field must be a number or null.", }, }, required: ['name'], @@ -430,84 +424,6 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, }); -const schemaFunction = { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string", - }, - args: { - type: 'object', - errorMessage: "The 'args' field must be an object", - }, - bindings: { - type: 'object', - properties: { - storage: { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, - }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - type: "The 'storage' field must be an object", - additionalProperties: 'No additional properties are allowed in the storage object', - required: "The 'bucket' field is required in the storage object", - }, - }, - }, - additionalProperties: false, - errorMessage: { - type: "The 'bindings' field must be an object", - additionalProperties: 'No additional properties are allowed in the bindings object', - }, - }, - }, - required: ['name', 'path'], - additionalProperties: false, -}; - -const schemaStorage = { - type: 'object', - properties: { - name: { - type: 'string', - minLength: 6, - maxLength: 63, - pattern: '^.{6,63}$', - errorMessage: "The 'name' field must be a string between 6 and 63 characters.", - }, - dir: { - type: 'string', - errorMessage: "The 'dir' field must be a string.", - }, - edgeAccess: { - type: 'string', - enum: ['read_only', 'read_write', 'restricted'], - errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", - }, - }, - required: ['name', 'dir'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in storage items.', - required: "The 'name' and 'dir' fields are required.", - }, -}; - const azionConfigSchema = { $id: 'azionConfig', definitions: { @@ -602,674 +518,640 @@ const azionConfigSchema = { additionalProperties: "No additional properties are allowed in the 'build' object", }, }, - edgeApplications: { + functions: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', - minLength: 1, - maxLength: 250, - errorMessage: "The 'name' field must be a string with 1 to 250 characters", + errorMessage: "The function's 'name' field must be a string", }, - modules: { + path: { + type: 'string', + errorMessage: "The function's 'path' field must be a string", + }, + args: { type: 'object', - properties: { - edgeCacheEnabled: { - type: 'boolean', - default: true, - errorMessage: "The 'edgeCacheEnabled' field must be a boolean", - }, - edgeFunctionsEnabled: { - type: 'boolean', - default: false, - errorMessage: "The 'edgeFunctionsEnabled' field must be a boolean", - }, - applicationAcceleratorEnabled: { - type: 'boolean', - default: false, - errorMessage: "The 'applicationAcceleratorEnabled' field must be a boolean", - }, - imageProcessorEnabled: { - type: 'boolean', - default: false, - errorMessage: "The 'imageProcessorEnabled' field must be a boolean", - }, - tieredCacheEnabled: { - type: 'boolean', - default: false, - errorMessage: "The 'tieredCacheEnabled' field must be a boolean", - }, - }, - required: [], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in modules object', - }, + additionalProperties: true, + errorMessage: "The function's 'args' field must be an object", }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", + }, + required: ['name', 'path'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in function items', + required: "Both 'name' and 'path' fields are required for each function", + }, + }, + }, + rules: { + type: 'object', + properties: { + request: { + type: 'array', + items: createRuleSchema(true), + }, + response: { + type: 'array', + items: createRuleSchema(false), + }, + }, + additionalProperties: false, + }, + origin: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'integer', + errorMessage: "The 'id' field must be a number.", }, - debug: { - type: 'boolean', - default: false, - errorMessage: "The 'debug' field must be a boolean", + key: { + type: 'string', + errorMessage: "The 'key' field must be a string.", }, - cache: { - type: 'array', - items: { - type: 'object', - properties: { - name: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + type: { + type: 'string', + enum: ['single_origin', 'object_storage', 'load_balancer', 'live_ingest'], + errorMessage: + "The 'type' field must be a string and one of 'single_origin', 'object_storage', 'load_balancer' or 'live_ingest'.", + }, + bucket: { + type: ['string', 'null'], + errorMessage: "The 'bucket' field must be a string or null.", + }, + prefix: { + type: ['string', 'null'], + errorMessage: "The 'prefix' field must be a string or null.", + }, + addresses: { + anyOf: [ + { + type: 'array', + items: { type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - stale: { - type: 'boolean', - errorMessage: "The 'stale' field must be a boolean.", - }, - queryStringSort: { - type: 'boolean', - errorMessage: "The 'queryStringSort' field must be a boolean.", - }, - methods: { - type: 'object', - properties: { - post: { - type: 'boolean', - errorMessage: "The 'post' field must be a boolean.", - }, - options: { - type: 'boolean', - errorMessage: "The 'options' field must be a boolean.", - }, - }, - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'methods' object.", - }, - }, - browser: { - type: 'object', - properties: { - maxAgeSeconds: { - oneOf: [ - { - type: 'number', - errorMessage: - "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", - }, - { - type: 'string', - pattern: '^[0-9+*/.() -]+$', - errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", - }, - ], - }, - }, - required: ['maxAgeSeconds'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'browser' object.", - required: "The 'maxAgeSeconds' field is required in the 'browser' object.", - }, - }, - edge: { - type: 'object', - properties: { - maxAgeSeconds: { - oneOf: [ - { - type: 'number', - errorMessage: - "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", - }, - { - type: 'string', - pattern: '^[0-9+*/.() -]+$', - errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", - }, - ], - }, - }, - required: ['maxAgeSeconds'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'edge' object.", - required: "The 'maxAgeSeconds' field is required in the 'edge' object.", - }, }, - cacheByCookie: { - type: 'object', - properties: { - option: { - type: 'string', - enum: ['ignore', 'varies', 'whitelist', 'blacklist'], - errorMessage: - "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'..", - }, - list: { - type: 'array', - items: { - type: 'string', - errorMessage: "Each item in 'list' must be a string.", - }, - errorMessage: { - type: "The 'list' field must be an array of strings.", - }, - }, - }, - required: ['option'], - additionalProperties: false, - errorMessage: { - additionalProperties: "No additional properties are allowed in the 'cacheByCookie' object.", - required: "The 'option' field is required in the 'cacheByCookie' object.", - }, - if: { - properties: { - option: { enum: ['whitelist', 'blacklist'] }, - }, - }, - then: { - required: ['list'], - errorMessage: { - required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", - }, - }, + errorMessage: { + type: "The 'addresses' field must be an array of strings.", }, - cacheByQueryString: { + }, + { + type: 'array', + items: { type: 'object', properties: { - option: { + address: { type: 'string', - enum: ['ignore', 'varies', 'whitelist', 'blacklist'], - errorMessage: - "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'.", + errorMessage: "The 'address' field must be a string.", }, - list: { - type: 'array', - items: { - type: 'string', - errorMessage: "Each item in 'list' must be a string.", - }, - errorMessage: { - type: "The 'list' field must be an array of strings.", - }, + weight: { + type: 'integer', }, }, - required: ['option'], + required: ['address'], additionalProperties: false, errorMessage: { - additionalProperties: - "No additional properties are allowed in the 'cacheByQueryString' object.", - required: "The 'option' field is required in the 'cacheByQueryString' object.", - }, - if: { - properties: { - option: { enum: ['whitelist', 'blacklist'] }, - }, - }, - then: { - required: ['list'], - errorMessage: { - required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", - }, + type: "The 'addresses' field must be an array of objects.", + additionalProperties: 'No additional properties are allowed in address items.', + required: "The 'address' field is required in each address item.", }, }, }, - required: ['name'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in cache item objects.', - required: "The 'name' field is required in each cache item.", - }, - }, + ], + }, + hostHeader: { + type: 'string', + errorMessage: "The 'hostHeader' field must be a string.", + }, + protocolPolicy: { + type: 'string', + enum: ['preserve', 'http', 'https'], + errorMessage: + "The 'protocolPolicy' field must be either 'http', 'https' or 'preserve'. Default is 'preserve'.", + }, + redirection: { + type: 'boolean', + errorMessage: "The 'redirection' field must be a boolean.", + }, + method: { + type: 'string', + enum: ['ip_hash', 'least_connections', 'round_robin'], + errorMessage: + "The 'method' field must be either 'ip_hash', 'least_connections' or 'round_robin'. Default is 'ip_hash'.", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string.", + }, + connectionTimeout: { + type: 'integer', + errorMessage: "The 'connectionTimeout' field must be a number. Default is 60.", + }, + timeoutBetweenBytes: { + type: 'integer', + errorMessage: "The 'timeoutBetweenBytes' field must be a number. Default is 120.", }, - rules: { + hmac: { type: 'object', properties: { - request: { - type: 'array', - items: createRuleSchema(true), + region: { + type: 'string', + errorMessage: "The 'region' field must be a string.", }, - response: { - type: 'array', - items: createRuleSchema(false), + accessKey: { + type: 'string', + errorMessage: "The 'accessKey' field must be a string.", + }, + secretKey: { + type: 'string', + errorMessage: "The 'secretKey' field must be a string.", }, }, + required: ['region', 'accessKey', 'secretKey'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the hmac object.', + required: "The 'region, accessKey and secretKey' fields are required in the hmac object.", + }, }, }, - required: ['name'], + required: ['name', 'type'], additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in origin item objects.', + required: "The 'name and type' field is required in each origin item.", + }, + }, + errorMessage: { + additionalProperties: "The 'origin' field must be an array of objects.", }, - errorMessage: "The 'edgeApplications' field must be an array of application objects", }, - workloads: { + cache: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', - minLength: 1, - maxLength: 100, - errorMessage: "The 'name' field must be a string between 1 and 100 characters", - }, - alternateDomains: { - type: 'array', - items: { type: 'string' }, - maxItems: 50, - errorMessage: "The 'alternateDomains' field must be an array of strings with max 50 items", - }, - edgeApplication: { - type: 'string', - errorMessage: "The 'edgeApplication' field must be a string", + errorMessage: "The 'name' field must be a string.", }, - active: { + stale: { type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", - }, - networkMap: { - type: 'string', - enum: WORKLOAD_NETWORK_MAP, - default: '1', - errorMessage: "The 'networkMap' must be either '1' or '2'", - }, - edgeFirewall: { - type: ['string', 'null'], - errorMessage: "The 'edgeFirewall' must be an integer or null", + errorMessage: "The 'stale' field must be a boolean.", }, - workloadHostnameAllowAccess: { + queryStringSort: { type: 'boolean', - default: true, - errorMessage: "The 'workloadHostnameAllowAccess' field must be a boolean", - }, - domains: { - type: 'array', - items: { - type: 'string', - minLength: 1, - maxLength: 250, - errorMessage: 'Each domain must be a string between 1 and 250 characters', - }, - minItems: 1, - errorMessage: "The 'domains' field must be an array of domain strings", + errorMessage: "The 'queryStringSort' field must be a boolean.", }, - tls: { + methods: { type: 'object', properties: { - certificate: { - type: ['integer', 'null'], - minimum: 1, - errorMessage: "The 'certificate' must be an integer >= 1 or null", - }, - ciphers: { - type: ['string', 'null'], - enum: [...WORKLOAD_TLS_CIPHERS, 'null'], - errorMessage: "The 'ciphers' must be a valid TLS cipher suite or null", + post: { + type: 'boolean', + errorMessage: "The 'post' field must be a boolean.", }, - minimumVersion: { - type: ['string', 'null'], - enum: [...WORKLOAD_TLS_VERSIONS, 'null'], - default: 'tls_1_2', - errorMessage: "The 'minimumVersion' must be a valid TLS version or null", + options: { + type: 'boolean', + errorMessage: "The 'options' field must be a boolean.", }, }, - default: { certificate: null, ciphers: null, minimumVersion: 'tls_1_2' }, additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'methods' object.", + }, }, - protocols: { + browser: { type: 'object', properties: { - http: { - type: 'object', - properties: { - versions: { - type: 'array', - items: { - type: 'string', - enum: WORKLOAD_HTTP_VERSIONS, - }, - default: ['http1', 'http2'], - }, - httpPorts: { - type: 'array', - items: { type: 'integer' }, - default: [80], + maxAgeSeconds: { + oneOf: [ + { + type: 'number', + errorMessage: "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", }, - httpsPorts: { - type: 'array', - items: { type: 'integer' }, - default: [443], - }, - quicPorts: { - type: ['array', 'null'], - items: { type: 'integer' }, + { + type: 'string', + pattern: '^[0-9+*/.() -]+$', + errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", }, - }, - required: ['versions', 'httpPorts', 'httpsPorts'], - additionalProperties: false, - }, - }, - default: { - http: { - versions: ['http1', 'http2'], - httpPorts: [80], - httpsPorts: [443], - quicPorts: null, + ], }, }, + required: ['maxAgeSeconds'], additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'browser' object.", + required: "The 'maxAgeSeconds' field is required in the 'browser' object.", + }, }, - mtls: { + edge: { type: 'object', properties: { - verification: { - type: 'string', - enum: WORKLOAD_MTLS_VERIFICATION, - default: 'enforce', - }, - certificate: { - type: ['integer', 'null'], - minimum: 1, - }, - crl: { - type: ['array', 'null'], - items: { type: 'integer' }, - maxItems: 100, + maxAgeSeconds: { + oneOf: [ + { + type: 'number', + errorMessage: "The 'maxAgeSeconds' field must be a number or a valid mathematical expression.", + }, + { + type: 'string', + pattern: '^[0-9+*/.() -]+$', + errorMessage: "The 'maxAgeSeconds' field must be a valid mathematical expression.", + }, + ], }, }, - required: ['verification'], + required: ['maxAgeSeconds'], additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'edge' object.", + required: "The 'maxAgeSeconds' field is required in the 'edge' object.", + }, }, - }, - required: ['name', 'edgeApplication', 'domains'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in workload items', - required: { - name: "The 'name' field is required in workloads", - edgeApplication: "The 'edgeApplication' field is required in workloads", - domains: "The 'domains' field is required in workloads", + cacheByCookie: { + type: 'object', + properties: { + option: { + type: 'string', + enum: ['ignore', 'varies', 'whitelist', 'blacklist'], + errorMessage: "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'..", + }, + list: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each item in 'list' must be a string.", + }, + errorMessage: { + type: "The 'list' field must be an array of strings.", + }, + }, + }, + required: ['option'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'cacheByCookie' object.", + required: "The 'option' field is required in the 'cacheByCookie' object.", + }, + if: { + properties: { + option: { enum: ['whitelist', 'blacklist'] }, + }, + }, + then: { + required: ['list'], + errorMessage: { + required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", + }, + }, + }, + + cacheByQueryString: { + type: 'object', + properties: { + option: { + type: 'string', + enum: ['ignore', 'varies', 'whitelist', 'blacklist'], + errorMessage: "The 'option' field must be one of 'ignore', 'varies', 'whitelist' or 'blacklist'.", + }, + list: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each item in 'list' must be a string.", + }, + errorMessage: { + type: "The 'list' field must be an array of strings.", + }, + }, + }, + required: ['option'], + additionalProperties: false, + errorMessage: { + additionalProperties: "No additional properties are allowed in the 'cacheByQueryString' object.", + required: "The 'option' field is required in the 'cacheByQueryString' object.", + }, + if: { + properties: { + option: { enum: ['whitelist', 'blacklist'] }, + }, + }, + then: { + required: ['list'], + errorMessage: { + required: "The 'list' field is required when 'option' is 'whitelist' or 'blacklist'.", + }, + }, }, }, + required: ['name'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in cache item objects.', + required: "The 'name' field is required in each cache item.", + }, + }, + errorMessage: { + additionalProperties: "The 'cache' field must be an array of objects.", }, - errorMessage: "The 'workloads' field must be an array of workloads items.", }, - purge: { + networkList: { type: 'array', items: { type: 'object', properties: { - type: { + id: { + type: 'number', + errorMessage: "The 'id' field must be a number.", + }, + listType: { type: 'string', - enum: ['url', 'cachekey', 'wildcard'], - errorMessage: "The 'type' field must be either 'url', 'cachekey' or 'wildcard'.", + enum: NETWORK_LIST_TYPES, + errorMessage: + "The 'listType' field must be a string. Accepted values are 'ip_cidr', 'asn' or 'countries'.", }, - items: { + listContent: { type: 'array', - minItems: 1, items: { - type: 'string', - errorMessage: "Each item in 'items' must be a string.", - }, - errorMessage: { - type: "The 'items' field must be an array of strings.", - minItems: 'The purge items array cannot be empty. At least one item must be specified.', + type: ['string', 'number'], + errorMessage: "The 'listContent' field must be an array of strings or numbers.", }, }, - layer: { - type: 'string', - enum: ['edge_cache', 'tiered_cache'], - errorMessage: "The 'layer' field must be either 'edge_cache' or 'tiered_cache'.", - }, }, - required: ['type', 'items'], + required: ['id', 'listType', 'listContent'], additionalProperties: false, errorMessage: { - additionalProperties: 'No additional properties are allowed in purge items.', - required: "The 'type and items' fields are required in each purge item.", + additionalProperties: 'No additional properties are allowed in network list items.', + required: "The 'id, listType and listContent' fields are required in each network list item.", }, }, }, - edgeFirewall: { + domain: { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + cnameAccessOnly: { + type: 'boolean', + errorMessage: "The 'cnameAccessOnly' field must be a boolean.", + }, + cnames: { + type: 'array', + items: { + type: 'string', + errorMessage: "Each item in 'cnames' must be a string.", + }, + errorMessage: { + type: "The 'cnames' field must be an array of strings.", + }, + }, + edgeApplicationId: { + type: 'number', + errorMessage: "The 'edgeApplicationId' field must be a number.", + }, + edgeFirewallId: { + type: 'number', + errorMessage: "The 'edgeFirewallId' field must be a number.", + }, + digitalCertificateId: { + type: ['string', 'number', 'null'], + errorMessage: + "The 'digitalCertificateId' field must be a string, number or null. If string, it must be 'lets_encrypt'.", + }, + active: { + type: 'boolean', + errorMessage: "The 'active' field must be a boolean.", + }, + mtls: { + type: 'object', + properties: { + verification: { + type: 'string', + enum: ['enforce', 'permissive'], + errorMessage: "The 'verification' field must be a string.", + }, + trustedCaCertificateId: { + type: 'number', + errorMessage: "The 'trustedCaCertificateId' field must be a number.", + }, + crlList: { + type: 'array', + items: { + type: 'number', + errorMessage: "Each item in 'crlList' must be a number.", + }, + errorMessage: { + type: "The 'crlList' field must be an array of numbers.", + }, + }, + }, + required: ['verification', 'trustedCaCertificateId'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in the mtls object.', + required: "The 'verification and trustedCaCertificateId' fields are required in the mtls object.", + }, + }, + }, + additionalProperties: false, + }, + purge: { type: 'array', items: { type: 'object', properties: { - name: { + type: { type: 'string', - errorMessage: "The edgeFirewall configuration must have a 'name' field of type string", + enum: ['url', 'cachekey', 'wildcard'], + errorMessage: "The 'type' field must be either 'url', 'cachekey' or 'wildcard'.", }, - domains: { + urls: { type: 'array', items: { type: 'string', - errorMessage: "Each domain in the edge firewall's domains list must be a string", + errorMessage: "Each item in 'urls' must be a string.", + }, + errorMessage: { + type: "The 'urls' field must be an array of strings.", }, }, - active: { - type: 'boolean', - errorMessage: "The firewall's 'active' field must be a boolean", - }, - debugRules: { - type: 'boolean', - errorMessage: "The firewall's 'debugRules' field must be a boolean", - }, - edgeFunctions: { - type: 'boolean', - errorMessage: "The firewall's 'edgeFunctions' field must be a boolean", - }, - networkProtection: { - type: 'boolean', - errorMessage: "The firewall's 'networkProtection' field must be a boolean", + method: { + type: 'string', + enum: ['delete'], + errorMessage: "The 'method' field must be either 'delete'. Default is 'delete'.", }, - waf: { - type: 'boolean', - errorMessage: "The firewall's 'waf' field must be a boolean", + layer: { + type: 'string', + enum: ['edge_caching', 'l2_caching'], + errorMessage: + "The 'layer' field must be either 'edge_caching' or 'l2_caching'. Default is 'edge_caching'.", }, - variable: { + }, + required: ['type', 'urls'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in purge items.', + required: "The 'type and urls' fields are required in each purge item.", + }, + }, + }, + firewall: { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The firewall configuration must have a 'name' field of type string", + }, + domains: { + type: 'array', + items: { type: 'string', - enum: FIREWALL_VARIABLES, - errorMessage: `The 'variable' field must be one of: ${FIREWALL_VARIABLES.join(', ')}`, + errorMessage: "Each domain in the firewall's domains list must be a string", }, - rules: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "Each firewall rule must have a 'name' field of type string", - }, - description: { - type: 'string', - errorMessage: "The rule's 'description' field must be a string", - }, - active: { - type: 'boolean', - errorMessage: "The rule's 'active' field must be a boolean", - }, - match: { - type: 'string', - errorMessage: "The rule's 'match' field must be a string containing a valid regex pattern", - }, - behavior: { - type: 'object', - properties: { - runFunction: { - type: 'string', - errorMessage: "The 'runFunction' behavior must be a string", - }, - setWafRuleset: { - type: 'object', - properties: { - wafMode: { - type: 'string', - enum: FIREWALL_WAF_MODES, - errorMessage: `The wafMode must be one of: ${FIREWALL_WAF_MODES.join(', ')}`, - }, - wafId: { - type: 'string', - errorMessage: 'The wafId must be a string', - }, - }, - required: ['wafMode', 'wafId'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the setWafRuleset object', - required: "Both 'wafMode' and 'wafId' fields are required in setWafRuleset", - }, - }, - setRateLimit: { - type: 'object', - properties: { - type: { - type: 'string', - enum: FIREWALL_RATE_LIMIT_TYPES, - errorMessage: `The rate limit type must be one of: ${FIREWALL_RATE_LIMIT_TYPES.join(', ')}`, - }, - limitBy: { - type: 'string', - enum: FIREWALL_RATE_LIMIT_BY, - errorMessage: `The rate limit must be applied by one of: ${FIREWALL_RATE_LIMIT_BY.join(', ')}`, - }, - averageRateLimit: { - type: 'string', - errorMessage: 'The averageRateLimit must be a string', - }, - maximumBurstSize: { - type: 'string', - errorMessage: 'The maximumBurstSize must be a string', - }, - }, - required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the setRateLimit object', - required: - "All fields ('type', 'limitBy', 'averageRateLimit', 'maximumBurstSize') are required in setRateLimit", + }, + active: { + type: 'boolean', + errorMessage: "The firewall's 'active' field must be a boolean", + }, + debugRules: { + type: 'boolean', + errorMessage: "The firewall's 'debugRules' field must be a boolean", + }, + edgeFunctions: { + type: 'boolean', + errorMessage: "The firewall's 'edgeFunctions' field must be a boolean", + }, + networkProtection: { + type: 'boolean', + errorMessage: "The firewall's 'networkProtection' field must be a boolean", + }, + waf: { + type: 'boolean', + errorMessage: "The firewall's 'waf' field must be a boolean", + }, + variable: { + type: 'string', + enum: FIREWALL_VARIABLES, + errorMessage: `The 'variable' field must be one of: ${FIREWALL_VARIABLES.join(', ')}`, + }, + rules: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "Each firewall rule must have a 'name' field of type string", + }, + description: { + type: 'string', + errorMessage: "The rule's 'description' field must be a string", + }, + active: { + type: 'boolean', + errorMessage: "The rule's 'active' field must be a boolean", + }, + match: { + type: 'string', + errorMessage: "The rule's 'match' field must be a string containing a valid regex pattern", + }, + behavior: { + type: 'object', + properties: { + runFunction: { + type: 'string', + errorMessage: "The 'runFunction' behavior must be a string", + }, + setWafRuleset: { + type: 'object', + properties: { + wafMode: { + type: 'string', + enum: FIREWALL_WAF_MODES, }, + wafId: { type: 'string' }, }, - deny: { - type: 'boolean', - errorMessage: 'The deny behavior must be a boolean', - }, - drop: { - type: 'boolean', - errorMessage: 'The drop behavior must be a boolean', - }, - setCustomResponse: { - type: 'object', - properties: { - statusCode: { - type: ['integer', 'string'], - minimum: 200, - maximum: 499, - errorMessage: 'The statusCode must be a number or string between 200 and 499', - }, - contentType: { - type: 'string', - errorMessage: 'The contentType must be a string', - }, - contentBody: { - type: 'string', - errorMessage: 'The contentBody must be a string', - }, + required: ['wafMode', 'wafId'], + }, + setRateLimit: { + type: 'object', + properties: { + type: { + type: 'string', + enum: FIREWALL_RATE_LIMIT_TYPES, }, - required: ['statusCode', 'contentType', 'contentBody'], - additionalProperties: false, - errorMessage: { - additionalProperties: - 'No additional properties are allowed in the setCustomResponse object', - required: - "All fields ('statusCode', 'contentType', 'contentBody') are required in setCustomResponse", + limitBy: { + type: 'string', + enum: FIREWALL_RATE_LIMIT_BY, }, + averageRateLimit: { type: 'string' }, + maximumBurstSize: { type: 'string' }, }, + required: ['type', 'limitBy', 'averageRateLimit', 'maximumBurstSize'], }, - not: { - anyOf: [ - { required: ['deny', 'drop'] }, - { required: ['deny', 'setCustomResponse'] }, - { required: ['deny', 'setRateLimit'] }, - { required: ['drop', 'setCustomResponse'] }, - { required: ['drop', 'setRateLimit'] }, - { required: ['setCustomResponse', 'setRateLimit'] }, - ], - }, - errorMessage: { - not: 'Cannot use multiple final behaviors (deny, drop, setRateLimit, setCustomResponse) together. You can combine non-final behaviors (runFunction, setWafRuleset) with only one final behavior.', + deny: { type: 'boolean' }, + drop: { type: 'boolean' }, + setCustomResponse: { + type: 'object', + properties: { + statusCode: { type: ['integer', 'string'], minimum: 200, maximum: 499 }, + contentType: { type: 'string' }, + contentBody: { type: 'string' }, + }, + required: ['statusCode', 'contentType', 'contentBody'], }, - additionalProperties: false, }, + not: { + anyOf: [ + { required: ['deny', 'drop'] }, + { required: ['deny', 'setCustomResponse'] }, + { required: ['deny', 'setRateLimit'] }, + { required: ['drop', 'setCustomResponse'] }, + { required: ['drop', 'setRateLimit'] }, + { required: ['setCustomResponse', 'setRateLimit'] }, + ], + }, + errorMessage: { + not: 'Cannot use multiple final behaviors (deny, drop, setRateLimit, setCustomResponse) together. You can combine non-final behaviors (runFunction, setWafRuleset) with only one final behavior.', + }, + additionalProperties: false, + }, + }, + required: ['name', 'behavior'], + oneOf: [ + { + anyOf: [{ required: ['match'] }, { required: ['variable'] }], + not: { required: ['criteria'] }, + errorMessage: "Cannot use 'match' or 'variable' together with 'criteria'.", }, - required: ['name', 'behavior'], - oneOf: [ - { + { + required: ['criteria'], + not: { anyOf: [{ required: ['match'] }, { required: ['variable'] }], - not: { required: ['criteria'] }, - errorMessage: "Cannot use 'match' or 'variable' together with 'criteria'.", - }, - { - required: ['criteria'], - not: { - anyOf: [{ required: ['match'] }, { required: ['variable'] }], - }, - errorMessage: "Cannot use 'criteria' together with 'match' or 'variable'.", }, - ], - errorMessage: { - oneOf: "You must use either 'match/variable' OR 'criteria', but not both at the same time", + errorMessage: "Cannot use 'criteria' together with 'match' or 'variable'.", }, + ], + errorMessage: { + oneOf: "You must use either 'match/variable' OR 'criteria', but not both at the same time", }, }, }, - required: ['name'], - additionalProperties: false, - errorMessage: { - type: 'Each edgeFirewall item must be an object', - additionalProperties: 'No additional properties are allowed in the edgeFirewall object', - required: "The 'name' field is required in each edge firewall object", - }, }, + required: ['name'], + additionalProperties: false, errorMessage: { - type: "The 'edgeFirewall' field must be an array of edge firewall objects", - }, - }, - networkList: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'number', - errorMessage: "The 'id' field must be a number.", - }, - listType: { - type: 'string', - enum: NETWORK_LIST_TYPES, - errorMessage: - "The 'listType' field must be a string. Accepted values are 'ip_cidr', 'asn' or 'countries'.", - }, - listContent: { - type: 'array', - items: { - type: ['string', 'number'], - errorMessage: "The 'listContent' field must be an array of strings or numbers.", - }, - }, - }, - required: ['id', 'listType', 'listContent'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in network list items.', - required: "The 'id, listType and listContent' fields are required in each network list item.", - }, + type: "The 'firewall' field must be an object", + additionalProperties: 'No additional properties are allowed in the firewall object', + required: "The 'name' field is required in the firewall object", }, }, waf: { @@ -1413,281 +1295,11 @@ const azionConfigSchema = { type: "The 'waf' field must be an array", }, }, - edgeConnectors: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - minLength: 1, - maxLength: 255, - errorMessage: "The 'name' field must be a string between 1 and 255 characters", - }, - modules: { - type: 'object', - properties: { - loadBalancerEnabled: { - type: 'boolean', - errorMessage: "'loadBalancerEnabled' must be a boolean", - }, - originShieldEnabled: { - type: 'boolean', - errorMessage: "'originShieldEnabled' must be a boolean", - }, - }, - required: ['originShieldEnabled', 'loadBalancerEnabled'], - additionalProperties: false, - errorMessage: { - required: "'loadBalancerEnabled' and 'originShieldEnabled' are required in modules", - additionalProperties: 'No additional properties are allowed in modules', - }, - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", - }, - type: { - type: 'string', - enum: EDGE_CONNECTOR_TYPES, - errorMessage: "The 'type' field must be one of: http, s3, edge_storage, live_ingest", - }, - typeProperties: { - oneOf: [ - { - type: 'object', - properties: { - versions: { - type: 'array', - items: { type: 'string' }, - errorMessage: "The 'versions' field must be an array of strings", - }, - host: { - type: 'string', - errorMessage: "The 'host' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string", - }, - followingRedirect: { - type: 'boolean', - errorMessage: "The 'followingRedirect' field must be a boolean", - }, - realIpHeader: { - type: 'string', - errorMessage: "The 'realIpHeader' field must be a string", - }, - realPortHeader: { - type: 'string', - errorMessage: "The 'realPortHeader' field must be a string", - }, - }, - required: ['versions', 'host', 'path'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in HTTP type properties', - required: "The 'versions', 'host', and 'path' fields are required for HTTP type", - }, - }, - { - type: 'object', - properties: { - endpoint: { - type: 'string', - errorMessage: "The 'endpoint' field must be a string", - }, - }, - required: ['endpoint'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in Live Ingest type properties', - required: "The 'endpoint' field is required for Live Ingest type", - }, - }, - { - type: 'object', - properties: { - host: { - type: 'string', - errorMessage: "The 'host' field must be a string", - }, - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string", - }, - region: { - type: 'string', - errorMessage: "The 'region' field must be a string", - }, - accessKey: { - type: 'string', - errorMessage: "The 'accessKey' field must be a string", - }, - secretKey: { - type: 'string', - errorMessage: "The 'secretKey' field must be a string", - }, - }, - required: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in S3 type properties', - required: 'All fields are required for S3 type', - }, - }, - { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, - }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in Storage type properties', - required: "The 'bucket' field is required for Storage type", - }, - }, - ], - }, - addresses: { - type: 'array', - items: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - maxLength: 255, - errorMessage: "The 'address' field must be a string between 1 and 255 characters", - }, - plainPort: { - type: 'integer', - minimum: 1, - maximum: 65535, - default: 80, - errorMessage: "The 'plainPort' field must be between 1 and 65535", - }, - tlsPort: { - type: 'integer', - minimum: 1, - maximum: 65535, - default: 443, - errorMessage: "The 'tlsPort' field must be between 1 and 65535", - }, - serverRole: { - type: 'string', - enum: ['primary', 'backup'], - default: 'primary', - errorMessage: "The 'serverRole' field must be either 'primary' or 'backup'", - }, - weight: { - type: 'integer', - minimum: 0, - maximum: 100, - default: 1, - errorMessage: "The 'weight' field must be between 0 and 100", - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'maxConns' field must be between 0 and 1000", - }, - maxConns: { - type: 'integer', - minimum: 0, - maximum: 1000, - default: 0, - errorMessage: "The 'maxFails' field must be between 1 and 10", - }, - maxFails: { - type: 'integer', - minimum: 1, - maximum: 10, - default: 1, - errorMessage: "The 'failTimeout' field must be between 1 and 60", - }, - failTimeout: { - type: 'integer', - minimum: 1, - maximum: 60, - default: 10, - errorMessage: "O campo 'failTimeout' deve estar entre 1 e 60", - }, - }, - required: ['address'], - additionalProperties: false, - }, - }, - tls: { - type: 'object', - properties: { - policy: { type: 'string' }, - }, - default: { policy: 'preserve' }, - }, - loadBalanceMethod: { - type: 'string', - enum: EDGE_CONNECTOR_LOAD_BALANCE, - default: 'off', - }, - connectionPreference: { - type: 'array', - items: { - type: 'string', - enum: EDGE_CONNECTOR_CONNECTION_PREFERENCE, - }, - maxItems: 2, - default: ['IPv6', 'IPv4'], - }, - connectionTimeout: { - type: 'integer', - minimum: 1, - maximum: 300, - default: 60, - }, - readWriteTimeout: { - type: 'integer', - minimum: 1, - maximum: 300, - default: 120, - }, - maxRetries: { - type: 'integer', - minimum: 0, - maximum: 10, - }, - }, - required: ['name', 'modules', 'type'], - additionalProperties: false, - }, - }, - edgeFunctions: { - type: 'array', - items: schemaFunction, - }, - edgeStorage: { - type: 'array', - items: schemaStorage, - errorMessage: "The 'edgeStorage' field must be an array of edge storage items.", - }, }, additionalProperties: false, errorMessage: { additionalProperties: - 'Config can only contain the following properties: build, edgeFunctions, edgeApplications, workloads, purge, edgefirewall, networkList, waf, edgeConnectors', + 'Config can only contain the following properties: build, functions, rules, origin, cache, networkList, domain, purge, firewall', type: 'Configuration must be an object', }, }, diff --git a/packages/config/src/configProcessor/helpers/schemaManifest.ts b/packages/config/src/configProcessor/helpers/schemaManifest.ts index 481c97cc..41d9e665 100644 --- a/packages/config/src/configProcessor/helpers/schemaManifest.ts +++ b/packages/config/src/configProcessor/helpers/schemaManifest.ts @@ -10,16 +10,16 @@ import { CACHE_BY_QUERY_STRING, CACHE_CDN_SETTINGS, CACHE_L2_REGION, - EDGE_CONNECTOR_CONNECTION_PREFERENCE, - EDGE_CONNECTOR_LOAD_BALANCE, - EDGE_CONNECTOR_TYPES, FIREWALL_BEHAVIOR_NAMES, FIREWALL_RATE_LIMIT_BY, FIREWALL_RATE_LIMIT_TYPES, FIREWALL_RULE_CONDITIONALS, FIREWALL_RULE_OPERATORS, FIREWALL_VARIABLES, + LOAD_BALANCER_METHODS, NETWORK_LIST_TYPES, + ORIGIN_PROTOCOL_POLICIES, + ORIGIN_TYPES, RULE_BEHAVIOR_NAMES, RULE_CONDITIONALS, RULE_OPERATORS_WITH_VALUE, @@ -28,11 +28,6 @@ import { RULE_VARIABLES, WAF_MODE, WAF_SENSITIVITY, - WORKLOAD_HTTP_VERSIONS, - WORKLOAD_MTLS_VERIFICATION, - WORKLOAD_NETWORK_MAP, - WORKLOAD_TLS_CIPHERS, - WORKLOAD_TLS_VERSIONS, } from '../../constants'; const schemaNetworkListManifest = { @@ -194,6 +189,99 @@ const schemaWafManifest = { }, }; +const schemaDomainsManifest = { + type: ['object', 'null'], + properties: { + id: { + type: 'number', + errorMessage: "The 'id' field must be a number.", + }, + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + edge_application_id: { + type: 'number', + errorMessage: "The 'edge_application_id' field must be a number.", + }, + cnames: { + type: 'array', + items: { + type: 'string', + }, + errorMessage: "The 'cnames' field must be an array of strings.", + }, + cname_access_only: { + type: 'boolean', + errorMessage: "The 'cname_access_only' field must be a boolean.", + }, + digital_certificate_id: { + oneOf: [{ type: 'number' }, { type: 'string', enum: ['lets_encrypt'] }, { type: 'null' }], + errorMessage: "The 'digital_certificate_id' field must be a number, 'lets_encrypt' or null.", + }, + is_active: { + type: 'boolean', + default: true, + errorMessage: "The 'is_active' field must be a boolean.", + }, + domain_name: { + type: 'string', + errorMessage: "The 'domain_name' field must be a string.", + }, + is_mtls_enabled: { + type: 'boolean', + errorMessage: "The 'is_mtls_enabled' field must be a boolean.", + }, + mtls_verification: { + type: 'string', + enum: ['enforce', 'permissive'], + errorMessage: "The 'mtls_verification' field must be either 'enforce' or 'permissive'.", + }, + mtls_trusted_ca_certificate_id: { + type: 'number', + errorMessage: "The 'mtls_trusted_ca_certificate_id' field must be a number.", + }, + edge_firewall_id: { + type: 'number', + errorMessage: "The 'edge_firewall_id' field must be a number.", + }, + crl_list: { + type: 'array', + items: { + type: 'number', + }, + errorMessage: "The 'crl_list' field must be an array of numbers.", + }, + }, + required: ['name'], + dependencies: { + is_mtls_enabled: { + oneOf: [ + { + properties: { + is_mtls_enabled: { enum: [false] }, + }, + }, + { + properties: { + is_mtls_enabled: { enum: [true] }, + }, + required: ['mtls_verification', 'mtls_trusted_ca_certificate_id'], + }, + ], + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in domain items.', + required: + "The 'name', 'edge_application_id', 'cnames', 'cname_access_only', and 'digital_certificate_id' fields are required.", + dependencies: { + is_mtls_enabled: "When mTLS is enabled, 'mtls_verification' and 'mtls_trusted_ca_certificate_id' are required.", + }, + }, +}; + const schemaFirewallRuleCriteria = { type: 'object', properties: { @@ -212,9 +300,9 @@ const schemaFirewallRuleCriteria = { enum: FIREWALL_RULE_CONDITIONALS, errorMessage: "The 'conditional' field must be one of: if, and, or", }, - argument: { + input_value: { type: 'string', - errorMessage: "The 'argument' field must be a string.", + errorMessage: "The 'input_value' field must be a string.", }, }, required: ['variable', 'operator', 'conditional'], @@ -225,34 +313,6 @@ const schemaFirewallRuleCriteria = { }, }; -const schemaStorageManifest = { - type: 'object', - properties: { - name: { - type: 'string', - minLength: 6, - maxLength: 63, - pattern: '^.{6,63}$', - errorMessage: "The 'name' field must be a string between 6 and 63 characters.", - }, - dir: { - type: 'string', - errorMessage: "The 'dir' field must be a string.", - }, - edge_access: { - type: 'string', - enum: ['read_only', 'read_write', 'restricted'], - errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", - }, - }, - required: ['name', 'dir'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in edge storage items.', - required: "The 'name' and 'dir' fields are required.", - }, -}; - const schemaFirewallRuleBehaviorArguments = { set_rate_limit: { type: 'object', @@ -284,58 +344,32 @@ const schemaFirewallRuleBehaviorArguments = { set_waf_ruleset: { type: 'object', properties: { - waf_id: { - type: 'integer', - errorMessage: 'The waf_id must be an integer', - }, + waf_id: { type: 'integer' }, mode: { type: 'string', enum: WAF_MODE, - errorMessage: `The mode must be one of: ${WAF_MODE.join(', ')}`, }, }, required: ['waf_id', 'mode'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the set_waf_ruleset object', - required: "Both 'waf_id' and 'mode' fields are required in set_waf_ruleset", - }, }, set_custom_response: { type: 'object', properties: { status_code: { oneOf: [ - { - type: 'integer', - minimum: 200, - maximum: 499, - errorMessage: 'The status_code must be an integer between 200 and 499', - }, - { - type: 'string', - pattern: '^[2-4][0-9]{2}$', - errorMessage: 'The status_code as string must be a valid HTTP status code (200-499)', - }, + { type: 'integer', minimum: 200, maximum: 499 }, + { type: 'string', pattern: '^[2-4][0-9]{2}$' }, ], - errorMessage: 'The status_code must be either an integer or string between 200 and 499', }, content_body: { type: 'string', maxLength: 500, - errorMessage: 'The content_body must be a string with maximum 500 characters', - }, - content_type: { - type: 'string', - errorMessage: 'The content_type must be a string', }, + content_type: { type: 'string' }, }, required: ['status_code', 'content_body', 'content_type'], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in the set_custom_response object', - required: "All fields ('status_code', 'content_body', 'content_type') are required in set_custom_response", - }, }, }; @@ -397,10 +431,14 @@ const schemaFirewallRule = { pattern: '^[\\u0000-\\uFFFF]*$', errorMessage: "The 'description' must not exceed 1000 characters and must not contain 4-byte unicode characters.", }, - active: { + is_active: { type: 'boolean', default: true, - errorMessage: "The 'active' field must be a boolean.", + errorMessage: "The 'is_active' field must be a boolean.", + }, + order: { + type: 'integer', + errorMessage: "The 'order' field must be an integer.", }, }, required: ['name', 'criteria', 'behaviors'], @@ -411,57 +449,6 @@ const schemaFirewallRule = { additionalProperties: 'No additional properties are allowed in firewall rules.', }, }; -const schemaFunctionManifest = { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string", - }, - arguemnt: { - type: 'string', - errorMessage: "The 'argument' field must be a string", - }, - args: { - type: 'object', - errorMessage: "The 'args' field must be an object", - }, - bindings: { - type: 'object', - properties: { - edge_storage: { - type: 'array', - items: { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, - }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in edge_storage items', - required: "The 'bucket' field is required", - }, - }, - errorMessage: "The 'edge_storage' field must be an array of storage bindings", - }, - }, - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in bindings object', - }, - }, - }, - required: ['name', 'target'], - additionalProperties: false, -}; const schemaFirewallManifest = { type: ['object', 'null'], @@ -572,6 +559,85 @@ const schemaApplicationCacheSettings = { additionalProperties: false, }; +const schemaApplicationOrigins = { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", + }, + origin_type: { + type: 'string', + enum: ORIGIN_TYPES, + errorMessage: + "The 'origin_type' field must be one of: single_origin, load_balancer, live_ingest, object_storage.", + }, + origin_protocol_policy: { + type: 'string', + enum: ORIGIN_PROTOCOL_POLICIES, + errorMessage: "The 'origin_protocol_policy' must be one of: preserve, http, https.", + }, + host_header: { + type: 'string', + errorMessage: "The 'host_header' field must be a string.", + }, + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string.", + }, + addresses: { + type: 'array', + items: { + type: 'object', + properties: { + address: { + type: 'string', + errorMessage: "The 'address' field must be a string.", + }, + weight: { + type: 'number', + minimum: 0, + maximum: 100, + errorMessage: "The 'weight' field must be a number between 0 and 100.", + }, + server_role: { + type: 'string', + enum: ['primary', 'backup'], + errorMessage: "The 'server_role' field must be either 'primary' or 'backup'.", + }, + }, + required: ['address'], + additionalProperties: false, + }, + }, + load_balancer: { + type: 'object', + properties: { + method: { + type: 'string', + enum: LOAD_BALANCER_METHODS, + errorMessage: "The 'method' field must be one of: ip_hash, least_connections, round_robin.", + }, + }, + required: ['method'], + additionalProperties: false, + }, + }, + required: ['name', 'origin_type', 'addresses'], + allOf: [ + { + if: { + properties: { origin_type: { const: 'object_storage' } }, + }, + then: { + required: ['bucket'], + errorMessage: "When origin_type is 'object_storage', the 'bucket' field is required.", + }, + }, + ], + additionalProperties: false, +}; + const schemaApplicationRules = { type: 'object', properties: { @@ -594,9 +660,9 @@ const schemaApplicationRules = { enum: RULE_BEHAVIOR_NAMES, errorMessage: "The 'name' field must be a valid behavior name.", }, - argument: { + target: { oneOf: [{ type: 'string' }, { type: 'null' }], - errorMessage: "The 'argument' must be a string or null.", + errorMessage: "The 'target' must be a string or null.", }, }, required: ['name'], @@ -625,9 +691,9 @@ const schemaApplicationRules = { enum: RULE_CONDITIONALS, errorMessage: "The 'conditional' field must be one of: if, and, or.", }, - argument: { + input_value: { type: 'string', - errorMessage: "The 'argument' field must be a string.", + errorMessage: "The 'input_value' field must be a string.", }, }, required: ['variable', 'operator', 'conditional'], @@ -638,8 +704,8 @@ const schemaApplicationRules = { properties: { operator: { enum: RULE_OPERATORS_WITH_VALUE }, }, - required: ['argument'], - errorMessage: "The operator 'matches' requires an argument.", + required: ['input_value'], + errorMessage: "The operator 'matches' requires an input_value.", }, ], }, @@ -653,525 +719,91 @@ const schemaApplicationRules = { additionalProperties: false, }; +// ... resto do arquivo mantido como está ... + const schemaApplicationManifest = { type: 'object', properties: { - name: { - type: 'string', - errorMessage: "The 'name' field must be a string.", - }, - delivery_protocol: { - type: 'string', - enum: APPLICATION_DELIVERY_PROTOCOLS, - default: 'http', - errorMessage: "The 'delivery_protocol' field must be either 'http,https' or 'http'.", - }, - http3: { - type: 'boolean', - errorMessage: "The 'http3' field must be a boolean.", - }, - http_port: { - type: 'array', - items: { - type: 'integer', - enum: APPLICATION_HTTP_PORTS, - }, - errorMessage: { - enum: "The 'http_port' field must be an array of valid HTTP ports.", - type: "The 'http_port' field must be an array", - }, - }, - https_port: { - type: 'array', - items: { - type: 'integer', - enum: APPLICATION_HTTPS_PORTS, - }, - default: [443], - errorMessage: "The 'https_port' field must be an array of valid HTTPS ports.", - }, - minimum_tls_version: { - type: 'string', - enum: APPLICATION_TLS_VERSIONS, - default: '', - errorMessage: "The 'minimum_tls_version' field must be a valid TLS version.", - }, - supported_ciphers: { - type: 'string', - enum: APPLICATION_SUPPORTED_CIPHERS, - default: 'all', - errorMessage: "The 'supported_ciphers' field must be a valid cipher suite.", - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean.", - }, - debug: { - type: 'boolean', - default: false, - errorMessage: "The 'debug' field must be a boolean.", - }, - modules: { + main_settings: { type: 'object', properties: { - edge_cache_enabled: { - type: 'boolean', - default: true, - errorMessage: "The 'edge_cache_enabled' field must be a boolean.", + name: { + type: 'string', + errorMessage: "The 'name' field must be a string.", }, - edge_functions_enabled: { - type: 'boolean', - default: false, - errorMessage: "The 'edge_functions_enabled' field must be a boolean.", + delivery_protocol: { + type: 'string', + enum: APPLICATION_DELIVERY_PROTOCOLS, + default: 'http', + errorMessage: "The 'delivery_protocol' field must be either 'http,https' or 'http'.", }, - application_accelerator_enabled: { + http3: { type: 'boolean', - default: false, - errorMessage: "The 'application_accelerator_enabled' field must be a boolean.", + errorMessage: "The 'http3' field must be a boolean.", }, - image_processor_enabled: { - type: 'boolean', - default: false, - errorMessage: "The 'image_processor_enabled' field must be a boolean.", + http_port: { + type: 'array', + items: { + type: 'integer', + enum: APPLICATION_HTTP_PORTS, + }, + errorMessage: { + enum: "The 'http_port' field must be an array of valid HTTP ports.", + type: "The 'http_port' field must be an array", + }, }, - tiered_cache_enabled: { + https_port: { + type: 'array', + items: { + type: 'integer', + enum: APPLICATION_HTTPS_PORTS, + }, + default: [443], + errorMessage: "The 'https_port' field must be an array of valid HTTPS ports.", + }, + minimum_tls_version: { + type: 'string', + enum: APPLICATION_TLS_VERSIONS, + default: '', + errorMessage: "The 'minimum_tls_version' field must be a valid TLS version.", + }, + supported_ciphers: { + type: 'string', + enum: APPLICATION_SUPPORTED_CIPHERS, + default: 'all', + errorMessage: "The 'supported_ciphers' field must be a valid cipher suite.", + }, + active: { type: 'boolean', - default: false, - errorMessage: "The 'tiered_cache_enabled' field must be a boolean.", + default: true, + errorMessage: "The 'active' field must be a boolean.", }, }, - required: [], additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in modules object.', - }, + required: ['name'], }, cache_settings: { type: 'array', items: schemaApplicationCacheSettings, errorMessage: "The 'cache_settings' field must be an array of cache setting items.", }, + origins: { + type: 'array', + items: schemaApplicationOrigins, + errorMessage: "The 'origins' field must be an array of origin items.", + }, rules: { type: 'array', items: schemaApplicationRules, errorMessage: "The 'rules' field must be an array of application rule items.", }, }, - required: ['name'], + required: ['main_settings'], additionalProperties: false, errorMessage: { additionalProperties: 'No additional properties are allowed in application items.', - required: "The 'name field are required.", - }, -}; - -const schemaWorkloadManifest = { - type: 'object', - properties: { - name: { - type: 'string', - minLength: 1, - maxLength: 100, - errorMessage: "The 'name' field must be a string between 1 and 100 characters", - }, - alternate_domains: { - type: 'array', - items: { type: 'string' }, - maxItems: 50, - errorMessage: "The 'alternate_domains' field must be an array of strings with max 50 items", - }, - edge_application: { - type: 'string', - errorMessage: "The 'edge_application' field must be a string", - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", - }, - network_map: { - type: 'string', - enum: WORKLOAD_NETWORK_MAP, - default: '1', - errorMessage: "The 'network_map' must be either '1' or '2'", - }, - edge_firewall: { - type: ['string', 'null'], - errorMessage: "The 'edge_firewall' must be an string or null", - }, - workload_hostname_allow_access: { - type: 'boolean', - default: true, - errorMessage: "The 'workload_hostname_allow_access' field must be a boolean", - }, - domains: { - type: 'array', - items: { - type: 'string', - minLength: 1, - maxLength: 250, - errorMessage: 'Each domain must be a string between 1 and 250 characters', - }, - minItems: 1, - errorMessage: "The 'domains' field must be an array of domain strings", - }, - tls: { - type: 'object', - properties: { - certificate: { - type: ['integer', 'null'], - minimum: 1, - errorMessage: "The 'certificate' must be an integer >= 1 or null", - }, - ciphers: { - type: ['string', 'null'], - enum: [...WORKLOAD_TLS_CIPHERS, null], - errorMessage: "The 'ciphers' must be a valid TLS cipher suite or null", - }, - minimum_version: { - type: ['string', 'null'], - enum: [...WORKLOAD_TLS_VERSIONS, null], - default: 'tls_1_2', - errorMessage: "The 'minimum_version' must be a valid TLS version or null", - }, - }, - default: { certificate: null, ciphers: null, minimum_version: 'tls_1_2' }, - additionalProperties: false, - }, - protocols: { - type: 'object', - properties: { - http: { - type: 'object', - properties: { - versions: { - type: 'array', - items: { - type: 'string', - enum: WORKLOAD_HTTP_VERSIONS, - }, - default: ['http1', 'http2'], - }, - http_ports: { - type: 'array', - items: { type: 'integer' }, - default: [80], - }, - https_ports: { - type: 'array', - items: { type: 'integer' }, - default: [443], - }, - quic_ports: { - type: ['array', 'null'], - items: { type: 'integer' }, - }, - }, - required: ['versions', 'http_ports', 'https_ports'], - additionalProperties: false, - }, - }, - default: { - http: { - versions: ['http1', 'http2'], - http_ports: [80], - https_ports: [443], - quic_ports: null, - }, - }, - additionalProperties: false, - }, - mtls: { - type: 'object', - properties: { - verification: { - type: 'string', - enum: WORKLOAD_MTLS_VERIFICATION, - default: 'enforce', - }, - certificate: { - type: ['integer', 'null'], - minimum: 1, - }, - crl: { - type: ['array', 'null'], - items: { type: 'integer' }, - maxItems: 100, - }, - }, - required: ['verification'], - additionalProperties: false, - }, - }, - required: ['name', 'edge_application', 'domains'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in workload items', - required: "The 'name', 'edge_application' and 'domains' fields are required in workload items", - }, -}; - -const schemaEdgeConnectorManifest = { - type: ['object', 'null'], - properties: { - name: { - type: 'string', - minLength: 1, - maxLength: 255, - errorMessage: "The 'name' field must be a string between 1 and 255 characters", - }, - modules: { - type: 'object', - properties: { - load_balancer_enabled: { - type: 'boolean', - errorMessage: "'load_balancer_enabled' must be a boolean", - }, - origin_shield_enabled: { - type: 'boolean', - errorMessage: "'origin_shield_enabled' must be a boolean", - }, - }, - required: ['load_balancer_enabled', 'origin_shield_enabled'], - additionalProperties: false, - errorMessage: { - required: "'load_balancer_enabled' and 'origin_shield_enabled' are required in modules", - additionalProperties: 'No additional properties are allowed in modules', - }, - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", - }, - type: { - type: 'string', - enum: EDGE_CONNECTOR_TYPES, - errorMessage: "The 'type' must be one of: http, s3, edge_storage, live_ingest", - }, - type_properties: { - oneOf: [ - { - type: 'object', - properties: { - versions: { - type: 'array', - items: { type: 'string' }, - errorMessage: "The 'versions' field must be an array of strings", - }, - host: { - type: 'string', - errorMessage: "The 'host' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string", - }, - following_redirect: { - type: 'boolean', - errorMessage: "The 'following_redirect' field must be a boolean", - }, - real_ip_header: { - type: 'string', - errorMessage: "The 'real_ip_header' field must be a string", - }, - real_port_header: { - type: 'string', - errorMessage: "The 'real_port_header' field must be a string", - }, - }, - required: ['versions', 'host', 'path'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in HTTP type properties', - required: "The 'versions', 'host', and 'path' fields are required for HTTP type", - }, - }, - { - type: 'object', - properties: { - endpoint: { - type: 'string', - errorMessage: "The 'endpoint' field must be a string", - }, - }, - required: ['endpoint'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in Live Ingest type properties', - required: "The 'endpoint' field is required for Live Ingest type", - }, - }, - { - type: 'object', - properties: { - host: { - type: 'string', - errorMessage: "The 'host' field must be a string", - }, - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The 'path' field must be a string", - }, - region: { - type: 'string', - errorMessage: "The 'region' field must be a string", - }, - access_key: { - type: 'string', - errorMessage: "The 'access_key' field must be a string", - }, - secret_key: { - type: 'string', - errorMessage: "The 'secret_key' field must be a string", - }, - }, - required: ['host', 'bucket', 'path', 'region', 'access_key', 'secret_key'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in S3 type properties', - required: 'All fields are required for S3 type', - }, - }, - { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, - }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in Storage type properties', - required: "The 'bucket' field is required for Storage type", - }, - }, - ], - }, - addresses: { - type: 'array', - items: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - maxLength: 255, - errorMessage: "The 'address' field must be a string between 1 and 255 characters", - }, - plain_port: { - type: 'integer', - minimum: 1, - maximum: 65535, - default: 80, - errorMessage: "The 'plain_port' must be between 1 and 65535", - }, - tls_port: { - type: 'integer', - minimum: 1, - maximum: 65535, - default: 443, - errorMessage: "The 'tls_port' must be between 1 and 65535", - }, - server_role: { - type: 'string', - enum: ['primary', 'backup'], - default: 'primary', - errorMessage: "The 'server_role' must be either 'primary' or 'backup'", - }, - weight: { - type: 'integer', - minimum: 0, - maximum: 100, - default: 1, - errorMessage: "The 'weight' must be between 0 and 100", - }, - active: { - type: 'boolean', - default: true, - errorMessage: "The 'active' field must be a boolean", - }, - max_conns: { - type: 'integer', - minimum: 0, - maximum: 1000, - default: 0, - errorMessage: "The 'max_conns' must be between 0 and 1000", - }, - max_fails: { - type: 'integer', - minimum: 1, - maximum: 10, - default: 1, - errorMessage: "The 'max_fails' must be between 1 and 10", - }, - fail_timeout: { - type: 'integer', - minimum: 1, - maximum: 60, - default: 10, - errorMessage: "The 'fail_timeout' must be between 1 and 60", - }, - }, - required: ['address'], - additionalProperties: false, - }, - }, - tls: { - type: 'object', - properties: { - policy: { type: 'string' }, - }, - default: { policy: 'preserve' }, - }, - load_balance_method: { - type: 'string', - enum: EDGE_CONNECTOR_LOAD_BALANCE, - default: 'off', - errorMessage: `The 'load_balance_method' must be one of: ${EDGE_CONNECTOR_LOAD_BALANCE.join(', ')}`, - }, - connection_preference: { - type: 'array', - items: { - type: 'string', - enum: EDGE_CONNECTOR_CONNECTION_PREFERENCE, - errorMessage: `Each connection preference must be one of: ${EDGE_CONNECTOR_CONNECTION_PREFERENCE.join(', ')}`, - }, - maxItems: 2, - default: ['IPv6', 'IPv4'], - errorMessage: "The 'connection_preference' field must be an array with maximum 2 items", - }, - connection_timeout: { - type: 'integer', - minimum: 1, - maximum: 300, - default: 60, - }, - read_write_timeout: { - type: 'integer', - minimum: 1, - maximum: 300, - default: 120, - }, - max_retries: { - type: 'integer', - minimum: 0, - maximum: 10, - }, + required: "The 'name' and 'main_settings' fields are required.", }, - required: ['name', 'modules', 'product_version', 'type'], - additionalProperties: false, }; const schemaManifest = { @@ -1189,41 +821,23 @@ const schemaManifest = { type: "The 'waf' field must be an array", }, }, - edge_firewall: { - type: 'array', - items: schemaFirewallManifest, + domain: { + ...schemaDomainsManifest, errorMessage: { - type: "The 'edge_firewall' field must be an array of edge firewall objects", + type: "The 'domain' field must be an object", }, }, - edge_applications: { - type: 'array', - items: schemaApplicationManifest, - errorMessage: "The 'edge_applications' field must be an array of edge application items.", - }, - workloads: { - type: 'array', - items: schemaWorkloadManifest, - errorMessage: "The 'workload' field must be an array of workloads items.", - }, - edge_connectors: { - type: 'array', - items: schemaEdgeConnectorManifest, + firewall: { + ...schemaFirewallManifest, errorMessage: { - type: "The 'edge_connectors' field must be an array", - }, - edge_storage: { - type: 'array', - items: schemaStorageManifest, - errorMessage: "The 'edge_storage' field must be an array of edge storage items.", - }, - edge_functions: { - type: 'array', - items: schemaFunctionManifest, - errorMessage: "The 'edge_functions' field must be an array of edge function items.", + type: "The 'firewall' field must be an object", }, }, + application: { + type: 'array', + items: schemaApplicationManifest, + errorMessage: "The 'application' field must be an array of application items.", + }, }, }; - export { schemaManifest }; diff --git a/packages/config/src/configProcessor/processConfig/index.test.ts b/packages/config/src/configProcessor/processConfig/index.test.ts index 9512a509..8cc3a0e7 100644 --- a/packages/config/src/configProcessor/processConfig/index.test.ts +++ b/packages/config/src/configProcessor/processConfig/index.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { processConfig } from '..'; -import { AzionConfig } from '../../config/types'; +import { AzionConfig } from '../../types'; describe('processConfig', () => { describe('Cache and Rules', () => { @@ -9,6 +9,9 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, + custom: { + minify: true, + }, }, }; expect(processConfig(config)).toEqual( @@ -16,6 +19,9 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, + custom: { + minify: true, + }, }, }), ); @@ -84,7 +90,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'rewrite_request', - argument: '/new-path', + target: '/new-path', }), ]), ); @@ -171,14 +177,14 @@ describe('processConfig', () => { criteria: expect.arrayContaining([ expect.arrayContaining([ expect.objectContaining({ - argument: '/api', + input_value: '/api', }), ]), ]), behaviors: expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - argument: 'my origin storage', + target: 'my origin storage', }), ]), }), @@ -246,7 +252,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'forward_cookies', - argument: null, // Updated from 'params' to 'argument' + target: null, // Updated from 'params' to 'target' }), ]), ); @@ -323,7 +329,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - argument: 'my origin storage', + target: 'my origin storage', }), ]), ); @@ -393,7 +399,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'run_function', - argument: 'handler', + target: 'handler', }), ]), ); @@ -547,7 +553,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_request_cookie', - argument: 'sessionId=abc123', + target: 'sessionId=abc123', }), ]), ); @@ -573,7 +579,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_request_header', - argument: 'Authorization: Bearer abc123', + target: 'Authorization: Bearer abc123', }), ]), ); @@ -962,7 +968,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - argument: 'my single origin', + target: 'my single origin', }), ]), ); @@ -1003,7 +1009,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'bypass_cache_phase', - argument: null, + target: null, }), ]), ); @@ -1029,7 +1035,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'redirect_to_301', - argument: 'https://example.com', + target: 'https://example.com', }), ]), ); @@ -1055,7 +1061,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'redirect_to_302', - argument: 'https://example.com', + target: 'https://example.com', }), ]), ); @@ -1085,7 +1091,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'capture_match_groups', - argument: { + target: { regex: '^/user/(.*)', captured_array: 'userId', // eslint-disable-next-line no-template-curly-in-string @@ -1116,7 +1122,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'filter_response_cookie', - argument: '_cookie', + target: '_cookie', }), ]), ); @@ -1142,7 +1148,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - argument: 'X-Test-Header: value', + target: 'X-Test-Header: value', }), ]), ); @@ -1168,11 +1174,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - argument: 'X-Frame-Options: DENY', + target: 'X-Frame-Options: DENY', }), expect.objectContaining({ name: 'add_response_header', - argument: "Content-Security-Policy: default-src 'self'", + target: "Content-Security-Policy: default-src 'self'", }), ]), ); @@ -1198,7 +1204,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'enable_gzip', - argument: '', + target: '', }), ]), ); @@ -1242,17 +1248,17 @@ describe('processConfig', () => { expect.objectContaining({ name: 'Example Rule', description: 'This rule redirects all traffic.', - active: false, + is_active: false, }), expect.objectContaining({ name: 'Second Rule', description: '', // Should default to an empty string - active: true, + is_active: true, }), expect.objectContaining({ name: 'Third Rule', description: 'This rule handles home traffic.', - active: true, // Should default to true + is_active: true, // Should default to true }), ]); }); @@ -1275,21 +1281,25 @@ describe('processConfig', () => { expect(result.rules[0]).toEqual( expect.objectContaining({ name: 'First Request Rule', + order: 2, }), ); expect(result.rules[1]).toEqual( expect.objectContaining({ name: 'Second Request Rule', + order: 3, }), ); expect(result.rules[2]).toEqual( expect.objectContaining({ name: 'First Response Rule', + order: 2, }), ); expect(result.rules[3]).toEqual( expect.objectContaining({ name: 'Second Response Rule', + order: 3, }), ); }); @@ -1326,14 +1336,14 @@ describe('processConfig', () => { expect(result.rules[0].behaviors).toEqual([ expect.objectContaining({ name: 'add_request_header', - argument: 'Authorization: Bearer abc123', + target: 'Authorization: Bearer abc123', }), expect.objectContaining({ name: 'deliver', }), expect.objectContaining({ name: 'set_origin', - argument: 'my origin storage', + target: 'my origin storage', }), ]); }); @@ -1387,11 +1397,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - argument: 'legacy origin', + target: 'legacy origin', }), expect.objectContaining({ name: 'add_request_header', - argument: 'Authorization: Bearer legacy', + target: 'Authorization: Bearer legacy', }), ]), ); @@ -1416,11 +1426,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - argument: 'X-Legacy-Header: legacy', + target: 'X-Legacy-Header: legacy', }), expect.objectContaining({ name: 'enable_gzip', - argument: '', + target: '', }), ]), ); @@ -1456,11 +1466,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'set_origin', - argument: 'mixed origin', + target: 'mixed origin', }), expect.objectContaining({ name: 'add_request_header', - argument: 'Authorization: Bearer mixed', + target: 'Authorization: Bearer mixed', }), ]), ); @@ -1487,11 +1497,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'add_response_header', - argument: 'X-Mixed-Header: mixed', + target: 'X-Mixed-Header: mixed', }), expect.objectContaining({ name: 'enable_gzip', - argument: '', + target: '', }), ]), ); @@ -1747,7 +1757,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -1765,7 +1775,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + input_value: '^/', }, ], ]); @@ -1783,7 +1793,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + input_value: '^/', }, ], behavior: { @@ -1816,13 +1826,13 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, { variable: '${device_group}', operator: 'is_equal', conditional: 'and', - argument: 'mobile', + inputValue: 'mobile', }, ], behavior: { @@ -1840,13 +1850,13 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + input_value: '^/', }, { variable: '${device_group}', operator: 'is_equal', conditional: 'and', - argument: 'mobile', + input_value: 'mobile', }, ], ]); @@ -1904,7 +1914,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -1921,11 +1931,11 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'filter_request_header', - argument: 'X-Test-Header', + target: 'X-Test-Header', }), expect.objectContaining({ name: 'filter_request_cookie', - argument: '_cookie', + target: '_cookie', }), ]), ); @@ -1944,7 +1954,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -1960,7 +1970,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'no_content', - argument: null, + target: null, }), ]), ); @@ -1979,7 +1989,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/', + inputValue: '^/', }, ], behavior: { @@ -1995,7 +2005,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'deliver', - argument: null, + target: null, }), ]), ); @@ -2014,7 +2024,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/images', + inputValue: '^/images', }, ], behavior: { @@ -2030,7 +2040,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'optimize_images', - argument: null, + target: null, }), ]), ); @@ -2049,7 +2059,7 @@ describe('processConfig', () => { variable: '${uri}', operator: 'matches', conditional: 'if', - argument: '^/login', + inputValue: '^/login', }, ], behavior: { @@ -2065,7 +2075,7 @@ describe('processConfig', () => { expect.arrayContaining([ expect.objectContaining({ name: 'deny', - argument: null, + target: null, }), ]), ); diff --git a/packages/config/src/configProcessor/processConfig/index.ts b/packages/config/src/configProcessor/processConfig/index.ts index bf589f92..f64d18f2 100644 --- a/packages/config/src/configProcessor/processConfig/index.ts +++ b/packages/config/src/configProcessor/processConfig/index.ts @@ -1,4 +1,5 @@ import { AzionConfig } from '../../types'; +import convertLegacyConfig from '../helpers/convertLegacyConfig'; import { factoryProcessContext } from '../processStrategy'; import { validateConfig } from '../validateConfig'; @@ -26,8 +27,9 @@ import { validateConfig } from '../validateConfig'; * const payloadCDN = processConfig(config); * console.log(payloadCDN); */ -function processConfig(config: AzionConfig) { +function processConfig(inputConfig: AzionConfig) { /* Converts legacy configuration properties to the new `behavior` format. */ + const config = convertLegacyConfig(inputConfig); validateConfig(config); // eslint-disable-next-line @typescript-eslint/no-explicit-any const payloadCDN: any = {}; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts deleted file mode 100644 index fa68d78a..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/application/edgeApplicationProcessConfigStrategy.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { AzionConfig, AzionEdgeApplication } from '../../../../types'; -import ProcessConfigStrategy from '../../processConfigStrategy'; -import CacheProcessConfigStrategy from './cacheProcessConfigStrategy'; -import RulesProcessConfigStrategy from './rulesProcessConfigStrategy'; - -class EdgeApplicationProcessConfigStrategy extends ProcessConfigStrategy { - private cacheStrategy = new CacheProcessConfigStrategy(); - private rulesStrategy = new RulesProcessConfigStrategy(); - - transformToManifest(config: AzionConfig) { - if (!config.edgeApplications || !Array.isArray(config.edgeApplications)) { - return []; - } - - return config.edgeApplications.map((app: AzionEdgeApplication) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const application: Record = { - name: app.name, - active: app.active ?? true, - debug: app.debug ?? false, - modules: { - edge_cache_enabled: app.edgeCacheEnabled ?? true, - edge_functions_enabled: app.edgeFunctionsEnabled ?? false, - application_accelerator_enabled: app.applicationAcceleratorEnabled ?? false, - image_processor_enabled: app.imageProcessorEnabled ?? false, - tiered_cache_enabled: app.tieredCacheEnabled ?? false, - }, - }; - - if (app.cache) { - application.cache_settings = this.cacheStrategy.transformToManifest(app.cache); - } - - if (app.rules) { - application.rules = this.rulesStrategy.transformToManifest( - app.rules, - config.edgeFunctions, - config.edgeConnectors, - ); - } - - return application; - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { - if (!payload.edgeApplications || !Array.isArray(payload.edgeApplications)) { - transformedPayload.edgeApplications = []; - return transformedPayload.edgeApplications; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformedPayload.edgeApplications = payload.edgeApplications.map((app: any) => { - return { - name: app.name, - active: app.active, - debug: app.debug, - edgeCacheEnabled: app.edge_cache_enabled, - edgeFunctionsEnabled: app.edge_functions_enabled, - applicationAcceleratorEnabled: app.application_accelerator_enabled, - imageProcessorEnabled: app.image_processor_enabled, - tieredCacheEnabled: app.tiered_cache_enabled, - cache: app.cache_settings ? this.cacheStrategy.transformToConfig(app.cache_settings) : undefined, - rules: app.rules ? this.rulesStrategy.transformToConfig(app.rules, transformedPayload) : undefined, - }; - }); - - return transformedPayload.edgeApplications; - } -} - -export default EdgeApplicationProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts similarity index 67% rename from packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts rename to packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts index fb93539b..c21fbbac 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/application/cacheProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/cacheProcessConfigStrategy.ts @@ -1,6 +1,6 @@ import { all, create } from 'mathjs'; -import { AzionCache } from '../../../../types'; -import ProcessConfigStrategy from '../../processConfigStrategy'; +import { AzionCache, AzionConfig } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; const math = create(all); @@ -22,16 +22,18 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { throw new Error(`Expression is not purely mathematical: ${expression}`); }; - transformToManifest(applicationCache: AzionCache[]) { - if (!Array.isArray(applicationCache) || applicationCache.length === 0) { - return []; + transformToManifest(config: AzionConfig) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const payload: any[] = []; + if (!Array.isArray(config?.cache) || config?.cache.length === 0) { + return; } - - return applicationCache.map((cache) => { + config?.cache.forEach((cache) => { const maxAgeSecondsBrowser = cache?.browser ? this.evaluateMathExpression(cache.browser.maxAgeSeconds) : 0; const maxAgeSecondsEdge = cache?.edge ? this.evaluateMathExpression(cache.edge.maxAgeSeconds) : 60; - return { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cacheSetting: any = { name: cache.name, browser_cache_settings: cache?.browser ? 'override' : 'honor', browser_cache_settings_maximum_ttl: maxAgeSecondsBrowser, @@ -41,16 +43,39 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { enable_caching_for_options: cache?.methods?.options || false, enable_query_string_sort: cache?.queryStringSort || false, }; + + if (cache.cacheByQueryString) { + cacheSetting.cache_by_query_string = + cache.cacheByQueryString.option === 'varies' ? 'all' : cache.cacheByQueryString.option; + if (cache.cacheByQueryString.option === 'whitelist' || cache.cacheByQueryString.option === 'blacklist') { + cacheSetting.query_string_fields = cache.cacheByQueryString.list || []; + } else { + cacheSetting.query_string_fields = []; + } + } + + if (cache.cacheByCookie) { + cacheSetting.cache_by_cookie = cache.cacheByCookie.option === 'varies' ? 'all' : cache.cacheByCookie.option; + if (cache.cacheByCookie.option === 'whitelist' || cache.cacheByCookie.option === 'blacklist') { + cacheSetting.cookie_names = cache.cacheByCookie.list || []; + } else { + cacheSetting.cookie_names = []; + } + } + + payload.push(cacheSetting); }); + return payload; } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(cacheSettings: any[]) { - if (!Array.isArray(cacheSettings) || cacheSettings.length === 0) { - return []; + transformToConfig(payload: any, transformedPayload: AzionConfig) { + const config = payload.cache; + if (!Array.isArray(config) || config.length === 0) { + return; } - - return cacheSettings.map((cache) => { + transformedPayload.cache = []; + config.forEach((cache) => { const maxAgeSecondsBrowser = cache.browser_cache_settings_maximum_ttl ? this.evaluateMathExpression(cache.browser_cache_settings_maximum_ttl) : 0; @@ -109,8 +134,9 @@ class CacheProcessConfigStrategy extends ProcessConfigStrategy { } } - return cacheSetting; + transformedPayload.cache!.push(cacheSetting); }); + return transformedPayload.cache; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts new file mode 100644 index 00000000..c7a1eee6 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/domainProcessConfigStrategy.ts @@ -0,0 +1,82 @@ +import { AzionConfig } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +/** + * DomainProcessConfigStrategy + * @class DomainProcessConfigStrategy + * @description This class is implementation of the Domain ProcessConfig Strategy. + */ +class DomainProcessConfigStrategy extends ProcessConfigStrategy { + transformToManifest(config: AzionConfig) { + const domain = config?.domain; + if (!domain || Object.keys(domain).length === 0) { + return; + } + if ( + domain.digitalCertificateId && + typeof domain.digitalCertificateId === 'string' && + domain.digitalCertificateId !== 'lets_encrypt' + ) { + throw new Error( + `Domain ${domain.name} has an invalid digital certificate ID: ${domain.digitalCertificateId}. Only 'lets_encrypt' or null is supported.`, + ); + } + + if ( + domain.mtls?.verification && + domain.mtls.verification !== 'enforce' && + domain.mtls.verification !== 'permissive' + ) { + throw new Error( + `Domain ${domain.name} has an invalid verification value: ${domain.mtls.verification}. Only 'enforce' or 'permissive' is supported.`, + ); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const domainSetting: any = { + name: domain.name, + cname_access_only: domain.cnameAccessOnly ?? false, + cnames: domain.cnames || [], + digital_certificate_id: domain.digitalCertificateId || null, + edge_application_id: domain.edgeApplicationId || null, + edge_firewall_id: domain.edgeFirewallId || null, + is_active: domain.active === undefined ? true : domain.active, + }; + if (domain.mtls) { + domainSetting.is_mtls_enabled = true; + domainSetting.mtls_verification = domain.mtls.verification; + domainSetting.mtls_trusted_ca_certificate_id = domain.mtls.trustedCaCertificateId; + domainSetting.crl_list = domain.mtls.crlList || []; + } else { + domainSetting.is_mtls_enabled = false; + } + return domainSetting; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: any, transformedPayload: AzionConfig) { + const domain = payload.domain; + if (!domain || Object.keys(domain).length === 0) { + return; + } + transformedPayload.domain = { + name: domain.name, + cnameAccessOnly: domain.cname_access_only, + cnames: domain.cnames, + digitalCertificateId: domain.digital_certificate_id, + edgeApplicationId: domain.edge_application_id, + edgeFirewallId: domain.edge_firewall_id, + active: domain.is_active === undefined ? true : domain.is_active, + mtls: domain.mtls_verification + ? { + verification: domain.mtls_verification, + trustedCaCertificateId: domain.mtls_trusted_ca_certificate_id, + crlList: domain.crl_list, + } + : undefined, + }; + return transformedPayload.domain; + } +} + +export default DomainProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts deleted file mode 100644 index cf081470..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/edgeConnectorProcessConfigStrategy.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { - EDGE_CONNECTOR_ALLOWED_PROPERTIES, - EDGE_CONNECTOR_CONNECTION_PREFERENCE, - EDGE_CONNECTOR_LOAD_BALANCE, - EDGE_CONNECTOR_SERVER_ROLE, -} from '../../../constants'; -import { - AzionConfig, - AzionEdgeConnector, - EdgeConnectorType, - EdgeConnectorTypeProperty, - HttpTypeProperty, - LiveIngestTypeProperty, - S3TypeProperty, - StorageTypeProperty, -} from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; - -class EdgeConnectorProcessConfigStrategy extends ProcessConfigStrategy { - transformToManifest(config: AzionConfig) { - const edgeConnectors = config?.edgeConnectors; - if (!edgeConnectors || edgeConnectors.length === 0) { - return; - } - - return edgeConnectors.map((connector: AzionEdgeConnector) => ({ - name: connector.name, - modules: { - load_balancer_enabled: connector.modules.loadBalancerEnabled, - origin_shield_enabled: connector.modules.originShieldEnabled, - }, - active: connector.active ?? true, - type: connector.type, - type_properties: this.transformTypePropertiesToSnakeCase(connector.type, connector.typeProperties), - addresses: connector.addresses?.map((addr) => ({ - address: addr.address, - plain_port: addr.plainPort ?? 80, - tls_port: addr.tlsPort ?? 443, - server_role: addr.serverRole ?? 'primary', - weight: addr.weight ?? 1, - active: addr.active ?? true, - max_conns: addr.maxConns ?? 0, - max_fails: addr.maxFails ?? 1, - fail_timeout: addr.failTimeout ?? 10, - })), - tls: connector.tls ?? { policy: 'preserve' }, - load_balance_method: connector.loadBalanceMethod ?? 'off', - connection_preference: connector.connectionPreference ?? ['IPv6', 'IPv4'], - connection_timeout: connector.connectionTimeout ?? 60, - read_write_timeout: connector.readWriteTimeout ?? 120, - max_retries: connector.maxRetries ?? 0, - })); - } - - private transformTypePropertiesToSnakeCase(type: EdgeConnectorType, properties: EdgeConnectorTypeProperty) { - if (!properties) { - throw new Error(`Edge Connector of type '${type}' requires 'typeProperties'. Please add the required properties: - - For type 'http': { versions: string[], host: string, path: string } - - For type 'live_ingest': { endpoint: string } - - For type 's3': { host: string, bucket: string, path: string, region: string, accessKey: string, secretKey: string } - - For type 'edge_storage': { bucket: string }`); - } - - switch (type) { - case 'http': { - const httpProps = properties as HttpTypeProperty; - return { - versions: httpProps.versions, - host: httpProps.host, - path: httpProps.path, - following_redirect: httpProps.followingRedirect, - real_ip_header: httpProps.realIpHeader, - real_port_header: httpProps.realPortHeader, - }; - } - case 'live_ingest': { - const liveProps = properties as LiveIngestTypeProperty; - return { - endpoint: liveProps.endpoint, - }; - } - case 's3': { - const s3Props = properties as S3TypeProperty; - return { - host: s3Props.host, - bucket: s3Props.bucket, - path: s3Props.path, - region: s3Props.region, - access_key: s3Props.accessKey, - secret_key: s3Props.secretKey, - }; - } - case 'edge_storage': { - const storageProps = properties as StorageTypeProperty; - return { - bucket: storageProps.bucket, - prefix: storageProps.prefix, - }; - } - default: - throw new Error(`Invalid Edge Connector type: ${type}`); - } - } - - private validateTypeProperties(type: EdgeConnectorType, properties: EdgeConnectorTypeProperty) { - // Check if there are any invalid properties for the type - const invalidProperties = Object.keys(properties).filter( - (prop) => !(EDGE_CONNECTOR_ALLOWED_PROPERTIES[type] as unknown as string[]).includes(prop), - ); - if (invalidProperties.length > 0) { - throw new Error(`Invalid properties for type ${type}: ${invalidProperties.join(', ')}`); - } - } - - transformToConfig( - payload: { - edge_connector?: Array<{ - name: string; - modules: { load_balancer_enabled: boolean; origin_shield_enabled: boolean }; - active?: boolean; - type: EdgeConnectorType; - type_properties: Record; - addresses?: Array<{ - address: string; - plain_port?: number; - tls_port?: number; - server_role?: string; - weight?: number; - active?: boolean; - max_conns?: number; - max_fails?: number; - fail_timeout?: number; - }>; - tls?: { policy: string }; - load_balance_method?: string; - connection_preference?: string[]; - connection_timeout?: number; - read_write_timeout?: number; - max_retries?: number; - }>; - }, - transformedPayload: AzionConfig, - ) { - if (!payload.edge_connector || payload.edge_connector.length === 0) { - return; - } - - transformedPayload.edgeConnectors = payload.edge_connector.map( - (connector: { - name: string; - modules: { load_balancer_enabled: boolean; origin_shield_enabled: boolean }; - active?: boolean; - type: EdgeConnectorType; - type_properties: Record; - addresses?: Array<{ - address: string; - plain_port?: number; - tls_port?: number; - server_role?: string; - weight?: number; - active?: boolean; - max_conns?: number; - max_fails?: number; - fail_timeout?: number; - }>; - tls?: { policy: string }; - load_balance_method?: string; - connection_preference?: string[]; - connection_timeout?: number; - read_write_timeout?: number; - max_retries?: number; - }) => { - const typeProperties = this.transformTypePropertiesFromSnakeCase(connector.type, connector.type_properties); - this.validateTypeProperties(connector.type, typeProperties); - - return { - name: connector.name, - modules: { - loadBalancerEnabled: connector.modules.load_balancer_enabled, - originShieldEnabled: connector.modules.origin_shield_enabled, - }, - active: connector.active, - type: connector.type, - typeProperties, - addresses: connector.addresses?.map((addr) => ({ - address: addr.address, - plainPort: addr.plain_port, - tlsPort: addr.tls_port, - serverRole: addr.server_role as (typeof EDGE_CONNECTOR_SERVER_ROLE)[number], - weight: addr.weight, - active: addr.active, - maxConns: addr.max_conns, - maxFails: addr.max_fails, - failTimeout: addr.fail_timeout, - })), - tls: connector.tls, - loadBalanceMethod: connector.load_balance_method as (typeof EDGE_CONNECTOR_LOAD_BALANCE)[number], - connectionPreference: - connector.connection_preference as (typeof EDGE_CONNECTOR_CONNECTION_PREFERENCE)[number][], - connectionTimeout: connector.connection_timeout, - readWriteTimeout: connector.read_write_timeout, - maxRetries: connector.max_retries, - }; - }, - ); - - return transformedPayload.edgeConnectors; - } - - private transformTypePropertiesFromSnakeCase( - type: EdgeConnectorType, - properties: Record, - ): EdgeConnectorTypeProperty { - switch (type) { - case 'http': - return { - versions: properties.versions as string[], - host: properties.host as string, - path: properties.path as string, - followingRedirect: properties.following_redirect as boolean, - realIpHeader: properties.real_ip_header as string, - realPortHeader: properties.real_port_header as string, - } as HttpTypeProperty; - case 'live_ingest': - return { - endpoint: properties.endpoint as string, - } as LiveIngestTypeProperty; - case 's3': - return { - host: properties.host as string, - bucket: properties.bucket as string, - path: properties.path as string, - region: properties.region as string, - accessKey: properties.access_key as string, - secretKey: properties.secret_key as string, - } as S3TypeProperty; - case 'edge_storage': - return { - bucket: properties.bucket as string, - prefix: properties.prefix as string, - } as StorageTypeProperty; - default: - throw new Error(`Invalid Edge Connector type: ${type}`); - } - } -} - -export default EdgeConnectorProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts index 1659329e..1168428a 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts @@ -7,76 +7,31 @@ import ProcessConfigStrategy from '../processConfigStrategy'; * @description This class is implementation of the Functions ProcessConfig Strategy. */ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { - private validateStorageBinding(config: AzionConfig, bucketName: string, functionName: string) { - if (!Array.isArray(config?.edgeStorage) || !config.edgeStorage.find((storage) => storage.name === bucketName)) { - throw new Error( - `Function "${functionName}" references storage bucket "${bucketName}" which is not defined in the storage configuration.`, - ); - } - } - transformToManifest(config: AzionConfig) { - if (!Array.isArray(config?.edgeFunctions) || config?.edgeFunctions.length === 0) { - return []; + if (!Array.isArray(config?.functions) || config?.functions.length === 0) { + return; } - return config.edgeFunctions.map((func) => { - // Validar se o bucket referenciado existe - if (func.bindings?.storage?.bucket) { - this.validateStorageBinding(config, func.bindings.storage.bucket, func.name); - } - - return { - name: func.name, - argument: func.path, - args: func.args || {}, - bindings: func.bindings - ? { - edge_storage: func.bindings.storage - ? { - bucket: func.bindings.storage.bucket, - prefix: func.bindings.storage.prefix, - } - : undefined, - } - : undefined, - }; - }); + return config.functions.map((func) => ({ + name: func.name, + target: func.path, + args: func.args || {}, + })); } // eslint-disable-next-line @typescript-eslint/no-explicit-any transformToConfig(payload: any, transformedPayload: AzionConfig) { - if (!Array.isArray(payload?.edgeFunction) || payload?.edgeFunction.length === 0) { + if (!Array.isArray(payload?.functions) || payload?.functions.length === 0) { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformedPayload.edgeFunctions = payload.functions.map((func: any) => { - const config = { - name: func.name, - path: func.argument, - args: func.args || {}, - bindings: func.bindings - ? { - storage: func.bindings.edge_storage - ? { - bucket: func.bindings.edge_storage.bucket, - prefix: func.bindings.edge_storage.prefix, - } - : undefined, - } - : undefined, - }; - - // Validar se o bucket referenciado existe - if (config.bindings?.storage?.bucket) { - this.validateStorageBinding(transformedPayload, config.bindings.storage.bucket, config.name); - } - - return config; - }); + transformedPayload.functions = payload.functions.map((func: any) => ({ + name: func.name, + path: func.target, + args: func.args || {}, + })); - return transformedPayload.edgeFunctions; + return transformedPayload.functions; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts new file mode 100644 index 00000000..4054ecb2 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/localProcessConfigStrategy.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import ProcessConfigStrategy from '../processConfigStrategy'; + +export type LocalConfig = { + version?: string; + environment?: string; + timestamp?: string; + // outras configurações que quisermos adicionar +}; + +class LocalProcessConfigStrategy extends ProcessConfigStrategy { + private localConfig: LocalConfig; + + constructor(localConfig?: LocalConfig) { + super(); + this.localConfig = { + version: localConfig?.version || '1.0.0', + environment: localConfig?.environment || process.env.NODE_ENV || 'development', + timestamp: localConfig?.timestamp || new Date().toISOString(), + }; + } + + transformToManifest(_config: unknown, transformedPayload: any) { + transformedPayload.local = this.localConfig; + return transformedPayload.local; + } + + transformToConfig() { + return; + } +} + +export default LocalProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts new file mode 100644 index 00000000..ea9ec172 --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/originProcessConfigStrategy.ts @@ -0,0 +1,126 @@ +import { AzionConfig, AzionOrigin } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +/** + * OriginProcessConfigStrategy + * @class OriginProcessConfigStrategy + * @description This class is implementation of the Origin ProcessConfig Strategy. + */ +class OriginProcessConfigStrategy extends ProcessConfigStrategy { + transformToManifest(config: AzionConfig) { + const payload: AzionOrigin[] = []; + if (!Array.isArray(config?.origin) || config?.origin.length === 0) { + return; + } + const originsType = ['single_origin', 'object_storage', 'load_balancer', 'live_ingest']; + config?.origin.forEach((origin) => { + if (originsType.indexOf(origin.type) === -1) { + throw new Error(`Rule setOrigin originType '${origin.type}' is not supported`); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const originSetting: any = { + id: origin.id, + key: origin.key, + name: origin.name, + origin_type: origin.type, + }; + + if (origin.type !== 'object_storage') { + if (origin.path === '/') { + throw new Error('Origin path cannot be "/". Please use empty string or "/path"'); + } + originSetting.origin_path = origin.path || ''; + originSetting.origin_protocol_policy = origin.protocolPolicy || 'preserve'; + originSetting.method = origin.method || 'ip_hash'; + originSetting.is_origin_redirection_enabled = origin.redirection ?? false; + originSetting.connection_timeout = origin.connectionTimeout || 60; + originSetting.timeout_between_bytes = origin.timeoutBetweenBytes || 120; + + if (origin.addresses && origin.addresses.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const addresses: any[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + origin?.addresses.forEach((address: any) => { + if (typeof address === 'string') { + addresses.push({ + address, + }); + return; + } + if (address?.weight < 0 || address?.weight > 10) { + throw new Error(`When origin type is ${origin.type}, weight must be between 0 and 10`); + } + addresses.push(address); + }); + originSetting.addresses = addresses; + } else { + throw new Error(`When origin type is ${origin.type}, addresses is required`); + } + + originSetting.host_header = origin.hostHeader || '${host}'; + if (origin?.hmac) { + originSetting.hmac_authentication = true; + originSetting.hmac_region_name = origin.hmac?.region; + originSetting.hmac_access_key = origin.hmac?.accessKey; + originSetting.hmac_secret_key = origin.hmac?.secretKey; + } + } else if (origin.type === 'object_storage') { + originSetting.bucket = origin.bucket; + originSetting.prefix = origin.prefix || ''; + } + + payload.push(originSetting); + }); + return payload; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: any, transformedPayload: AzionConfig) { + const config = payload.origin; + if (!Array.isArray(config) || config.length === 0) { + return; + } + transformedPayload.origin = []; + const originsType = ['single_origin', 'object_storage', 'load_balancer', 'live_ingest']; + config.forEach((origin) => { + if (originsType.indexOf(origin.origin_type) === -1) { + throw new Error(`originType '${origin.origin_type}' is not supported`); + } + const originSetting: AzionOrigin = { + id: origin.id, + key: origin.key, + name: origin.name, + type: origin.origin_type, + }; + + if (originSetting.type !== 'object_storage') { + if (origin.path === '/') { + throw new Error('Origin path cannot be "/". Please use empty string or "/path"'); + } + originSetting.path = origin.origin_path; + originSetting.protocolPolicy = origin.origin_protocol_policy; + originSetting.method = origin.method; + originSetting.redirection = origin.is_origin_redirection_enabled ?? false; + originSetting.connectionTimeout = origin.connection_timeout; + originSetting.timeoutBetweenBytes = origin.timeout_between_bytes; + originSetting.addresses = origin.addresses; + originSetting.hostHeader = origin.host_header; + if (origin.hmac_authentication) { + originSetting.hmac = { + region: origin.hmac_region_name, + accessKey: origin.hmac_access_key, + secretKey: origin.hmac_secret_key, + }; + } + } else if (originSetting.type === 'object_storage') { + originSetting.bucket = origin.bucket; + originSetting.prefix = origin.prefix; + } + + transformedPayload.origin!.push(originSetting); + }); + return transformedPayload.origin; + } +} + +export default OriginProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts index d7a77aa1..f27e52da 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/purgeProcessConfigStrategy.ts @@ -14,8 +14,8 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { return; } config?.purge.forEach((purge) => { - purge?.items.forEach((value) => { - if (purge.type === 'url' && !value.includes('http://') && !value.includes('https://')) { + purge?.urls.forEach((value) => { + if (!value.includes('http://') && !value.includes('https://')) { throw new Error('The URL must contain the protocol (http:// or https://).'); } @@ -27,11 +27,12 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any const purgeSetting: any = { type: purge.type, - items: purge.items || [], + urls: purge.urls || [], + method: purge.method || 'delete', }; - if (purge?.layer) { - purgeSetting.layer = purge.layer; + if (purge?.type === 'cachekey') { + purgeSetting.layer = purge.layer || 'edge_caching'; } payload.push(purgeSetting); @@ -47,8 +48,8 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { } transformedPayload.purge = []; purgeConfig.forEach((purge) => { - purge.items.forEach((value: string) => { - if (purge.type === 'url' && !value.includes('http://') && !value.includes('https://')) { + purge.urls.forEach((value: string) => { + if (!value.includes('http://') && !value.includes('https://')) { throw new Error('The URL must contain the protocol (http:// or https://).'); } @@ -58,11 +59,12 @@ class PurgeProcessConfigStrategy extends ProcessConfigStrategy { }); const purgeSetting: AzionPurge = { type: purge.type, - items: purge.items || [], + urls: purge.urls || [], + method: purge.method || 'delete', }; - if (purge?.layer) { - purgeSetting.layer = purge.layer; + if (purge?.type === 'cachekey') { + purgeSetting.layer = purge.layer || 'edge_caching'; } transformedPayload.purge!.push(purgeSetting); diff --git a/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts similarity index 69% rename from packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts rename to packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts index f85595f8..21c8da4f 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/application/rulesProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/rulesProcessConfigStrategy.ts @@ -1,17 +1,11 @@ -import { - AzionConfig, - AzionEdgeConnector, - AzionEdgeFirewallCriteriaWithValue, - AzionEdgeFunction, - AzionRules, -} from '../../../../types'; +import { AzionConfig, AzionFirewallCriteriaWithValue } from '../../../types'; import { requestBehaviors, responseBehaviors, revertRequestBehaviors, revertResponseBehaviors, -} from '../../../helpers/behaviors'; -import ProcessConfigStrategy from '../../processConfigStrategy'; +} from '../../helpers/behaviors'; +import ProcessConfigStrategy from '../processConfigStrategy'; /** * RulesProcessConfigStrategy @@ -45,14 +39,14 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } } - private validateFunctionReferences(applicationRules: AzionRules, functions?: AzionEdgeFunction[]) { - if (!applicationRules?.request || !functions) { + private validateFunctionReferences(config: AzionConfig) { + if (!config?.rules?.request || !config?.functions) { return; } - const definedFunctions = new Set(functions.map((f) => f.name)); + const definedFunctions = new Set(config.functions.map((f) => f.name)); - for (const rule of applicationRules.request) { + for (const rule of config.rules.request) { if (rule.behavior?.runFunction) { if (!definedFunctions.has(rule.behavior.runFunction)) { throw new Error( @@ -64,38 +58,33 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToManifest( - applicationRules: AzionRules, - functions?: AzionEdgeFunction[], - edgeConnectors?: AzionEdgeConnector[], - ) { - // Validar referências de funções - this.validateFunctionReferences(applicationRules, functions); + transformToManifest(config: AzionConfig, context: any) { + // Validar referências de funções antes de transformar + this.validateFunctionReferences(config); // eslint-disable-next-line @typescript-eslint/no-explicit-any const payload: any[] = []; - if (!applicationRules || Object.keys(applicationRules).length === 0) { + if (config?.rules === undefined || Object.keys(config.rules).length === 0) { return; } - // request - if (Array.isArray(applicationRules?.request)) { - applicationRules.request.forEach((rule) => { + if (Array.isArray(config?.rules?.request)) { + config?.rules?.request?.forEach((rule, index) => { const cdnRule = { name: rule.name, phase: 'request', description: rule.description ?? '', - active: rule.active !== undefined ? rule.active : true, // Default to true if not provided - + is_active: rule.active !== undefined ? rule.active : true, // Default to true if not provided + order: index + 2, // index starts at 2, because the default rule is index 1 criteria: rule.criteria ? [ rule.criteria.map((criterion) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + const isWithValue = 'inputValue' in criterion; + const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, - ...(isWithValue && { argument: argument }), + ...(isWithValue && { input_value: inputValue }), }; }), ] @@ -105,34 +94,35 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { variable: rule.variable?.startsWith('${') ? rule.variable : `\${${rule.variable ?? 'uri'}}`, operator: 'matches', conditional: 'if', - argument: rule.match, + input_value: rule.match, }, ], ], behaviors: [], }; - this.addBehaviors(cdnRule, rule.behavior, requestBehaviors, { edgeConnectors }); + this.addBehaviors(cdnRule, rule.behavior, requestBehaviors, context); payload.push(cdnRule); }); } // response - if (Array.isArray(applicationRules?.response)) { - applicationRules.response.forEach((rule) => { + if (Array.isArray(config?.rules?.response)) { + config?.rules?.response.forEach((rule, index) => { const cdnRule = { name: rule.name, phase: 'response', description: rule.description ?? '', - active: rule.active !== undefined ? rule.active : true, // Default to true if not provided + is_active: rule.active !== undefined ? rule.active : true, // Default to true if not provided + order: index + 2, // index starts at 2, because the default rule is index 1 criteria: rule.criteria ? [ rule.criteria.map((criterion) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; + const isWithValue = 'inputValue' in criterion; + const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; return { ...rest, variable: criterion.variable.startsWith('${') ? criterion.variable : `\${${criterion.variable}}`, - ...(isWithValue && { argument: argument }), + ...(isWithValue && { input_value: inputValue }), }; }), ] @@ -142,13 +132,13 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { variable: rule.variable?.startsWith('${') ? rule.variable : `\${${rule.variable ?? 'uri'}}`, operator: 'matches', conditional: 'if', - argument: rule.match, + input_value: rule.match, }, ], ], behaviors: [], }; - this.addBehaviors(cdnRule, rule.behavior, responseBehaviors, { edgeConnectors }); + this.addBehaviors(cdnRule, rule.behavior, responseBehaviors, context); payload.push(cdnRule); }); } @@ -157,16 +147,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(rulesPayload: any[], transformedPayload: AzionConfig) { - if (!Array.isArray(rulesPayload)) { - return undefined; - } - - const rules: AzionRules = { - request: [], - response: [], - }; - + transformToConfig(payload: any, transformedPayload: AzionConfig) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const addBehaviorsObject = (behaviors: any, behaviorDefinitions: any, context: any) => { if (behaviors && Array.isArray(behaviors)) { @@ -196,42 +177,52 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { return undefined; }; + const rulesConfig = payload.rules; + if (!rulesConfig || Object.keys(rulesConfig).length === 0) { + return; + } + + transformedPayload.rules = { + request: [], + response: [], + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any - rulesPayload.forEach((rule: any) => { + rulesConfig.forEach((rule: any) => { if (rule.phase === 'request') { - rules.request!.push({ + transformedPayload.rules!.request!.push({ name: rule.name, description: rule.description, - active: rule.active, + active: rule.is_active, criteria: // Verifica se criteria existe e é um array de arrays Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) ? // eslint-disable-next-line @typescript-eslint/no-explicit-any rule.criteria[0].map((criterion: any) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion; + const isWithValue = 'input_value' in criterion; + const { input_value, ...rest } = criterion; return { ...rest, - ...(isWithValue && { argument: argument }), + ...(isWithValue && { inputValue: input_value }), }; }) : [], behavior: addBehaviorsObject(rule.behaviors, revertRequestBehaviors, transformedPayload), }); } else if (rule.phase === 'response') { - rules.response!.push({ + transformedPayload.rules!.response!.push({ name: rule.name, description: rule.description, - active: rule.active, + active: rule.is_active, criteria: Array.isArray(rule.criteria) && Array.isArray(rule.criteria[0]) ? // eslint-disable-next-line @typescript-eslint/no-explicit-any rule.criteria[0].map((criterion: any) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion; + const isWithValue = 'input_value' in criterion; + const { input_value, ...rest } = criterion; return { ...rest, - ...(isWithValue && { argument: argument }), + ...(isWithValue && { inputValue: input_value }), }; }) : [], @@ -240,7 +231,7 @@ class RulesProcessConfigStrategy extends ProcessConfigStrategy { } }); - return rules; + return transformedPayload.rules; } } diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts index a39280f1..a9585d9e 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.test.ts @@ -11,111 +11,171 @@ describe('FirewallProcessConfigStrategy', () => { describe('transformToManifest', () => { it('should transform a complete firewall config to manifest', () => { const config: AzionConfig = { - edgeFirewall: [ - { - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: true, - networkProtection: true, - waf: true, - debugRules: true, - rules: [ - { - name: 'Test Rule', - description: 'Test Description', - active: true, - match: '/test', - variable: 'uri', - criteria: [ - { - variable: 'uri', - conditional: 'if', - operator: 'matches', - argument: '/test', - }, - ], - behavior: { - deny: true, - }, - }, - ], - }, - ], - }; - - const manifest = strategy.transformToManifest(config); - expect(manifest).toEqual([ - { - main_settings: { - name: 'Test Firewall', - domains: ['example.com'], - is_active: true, - edge_functions_enabled: true, - network_protection_enabled: true, - waf_enabled: true, - debug_rules: true, - }, - rules_engine: [ + firewall: { + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: true, + networkProtection: true, + waf: true, + debugRules: true, + rules: [ { name: 'Test Rule', description: 'Test Description', - is_active: true, - behaviors: [{ name: 'deny', target: '' }], + active: true, + match: '/test', + variable: 'uri', criteria: [ - [ - { - variable: 'uri', - conditional: 'if', - operator: 'matches', - argument: '/test', - }, - ], + { + variable: 'uri', + conditional: 'if', + operator: 'matches', + inputValue: '/test', + }, ], + behavior: { + deny: true, + }, }, ], }, - ]); + }; + + const manifest = strategy.transformToManifest(config); + expect(manifest).toEqual({ + main_settings: { + name: 'Test Firewall', + domains: ['example.com'], + is_active: true, + edge_functions_enabled: true, + network_protection_enabled: true, + waf_enabled: true, + debug_rules: true, + }, + rules_engine: [ + { + name: 'Test Rule', + description: 'Test Description', + is_active: true, + behaviors: [{ name: 'deny', target: '' }], + criteria: [ + [ + { + variable: 'uri', + conditional: 'if', + operator: 'matches', + input_value: '/test', + }, + ], + ], + }, + ], + }); }); it('should handle firewall config without rules', () => { const config: AzionConfig = { - edgeFirewall: [ + firewall: { + name: 'Test Firewall', + domains: ['example.com'], + active: true, + }, + }; + + const manifest = strategy.transformToManifest(config); + expect(manifest).toEqual({ + main_settings: { + name: 'Test Firewall', + domains: ['example.com'], + is_active: true, + edge_functions_enabled: false, + network_protection_enabled: false, + waf_enabled: false, + debug_rules: false, + }, + }); + }); + + it('should return empty object when no firewall config is provided', () => { + const config: AzionConfig = {}; + const manifest = strategy.transformToManifest(config); + expect(manifest).toBeUndefined(); + }); + + it('should transform all behavior types correctly', () => { + const config: AzionConfig = { + functions: [ { - name: 'Test Firewall', - domains: ['example.com'], - active: true, + name: 'function1', + path: '.edge/functions/function1.js', }, ], + firewall: { + name: 'Test Firewall', + rules: [ + { + name: 'All Behaviors Rule', + active: true, + behavior: { + runFunction: 'function1', + setWafRuleset: { + wafMode: 'learning', + wafId: '123', + }, + setRateLimit: { + type: 'second', + limitBy: 'clientIp', + averageRateLimit: '1000', + maximumBurstSize: '1000', + }, + setCustomResponse: { + statusCode: 403, + contentType: 'text/plain', + contentBody: 'Blocked', + }, + }, + }, + ], + }, }; const manifest = strategy.transformToManifest(config); - expect(manifest).toEqual([ + expect(manifest.rules_engine[0].behaviors).toEqual([ { - main_settings: { - name: 'Test Firewall', - domains: ['example.com'], - is_active: true, - edge_functions_enabled: false, - network_protection_enabled: false, - waf_enabled: false, - debug_rules: false, + name: 'run_function', + target: 'function1', + }, + { + name: 'set_waf_ruleset', + target: { + mode: 'learning', + waf_id: '123', + }, + }, + { + name: 'set_rate_limit', + target: { + type: 'second', + limit_by: 'clientIp', + }, + }, + { + name: 'set_custom_response', + target: { + status_code: 403, + content_type: 'text/plain', + content_body: 'Blocked', }, }, ]); }); - - it('should return undefined when no firewall config is provided', () => { - const config: AzionConfig = {}; - const manifest = strategy.transformToManifest(config); - expect(manifest).toBeUndefined(); - }); }); describe('transformToConfig', () => { it('should transform a complete manifest to config', () => { - const manifest = [ - { + const manifest = { + firewall: { main_settings: { name: 'Test Firewall', domains: ['example.com'], @@ -136,7 +196,7 @@ describe('FirewallProcessConfigStrategy', () => { variable: '${uri}', conditional: 'if', operator: 'matches', - argument: '/test', + input_value: '/test', }, ], ], @@ -144,66 +204,62 @@ describe('FirewallProcessConfigStrategy', () => { }, ], }, - ]; + }; const config = {}; - const result = strategy.transformToConfig({ edgeFirewall: manifest }, config); + const result = strategy.transformToConfig(manifest, config); - expect(result).toEqual([ - { - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: true, - networkProtection: true, - waf: true, - debugRules: true, - rules: [ - { - name: 'Test Rule', - description: 'Test Description', - active: true, - criteria: [ - { - variable: '${uri}', - conditional: 'if', - operator: 'matches', - argument: '/test', - }, - ], - behavior: { - deny: true, + expect(result).toEqual({ + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: true, + networkProtection: true, + waf: true, + debugRules: true, + rules: [ + { + name: 'Test Rule', + description: 'Test Description', + active: true, + criteria: [ + { + variable: '${uri}', + conditional: 'if', + operator: 'matches', + inputValue: '/test', }, + ], + behavior: { + deny: true, }, - ], - }, - ]); + }, + ], + }); }); it('should handle manifest without rules', () => { - const manifest = [ - { + const manifest = { + firewall: { main_settings: { name: 'Test Firewall', domains: ['example.com'], is_active: true, }, }, - ]; + }; const config = {}; - const result = strategy.transformToConfig({ edgeFirewall: manifest }, config); - expect(result).toEqual([ - { - name: 'Test Firewall', - domains: ['example.com'], - active: true, - edgeFunctions: false, - networkProtection: false, - waf: false, - debugRules: false, - }, - ]); + const result = strategy.transformToConfig(manifest, config); + expect(result).toEqual({ + name: 'Test Firewall', + domains: ['example.com'], + active: true, + edgeFunctions: false, + networkProtection: false, + waf: false, + debugRules: false, + }); }); it('should return undefined when no firewall manifest is provided', () => { @@ -212,5 +268,116 @@ describe('FirewallProcessConfigStrategy', () => { const result = strategy.transformToConfig(manifest, config); expect(result).toStrictEqual(expect.objectContaining({})); }); + + it('should transform all behavior types from manifest to config', () => { + const manifest = { + firewall: { + name: 'Test Firewall', + rules_engine: [ + { + name: 'All Behaviors Rule', + is_active: true, + behaviors: [ + { + name: 'run_function', + target: 'function1', + }, + { + name: 'set_waf_ruleset', + target: { + mode: 'learning', + waf_id: '123', + }, + }, + { + name: 'set_rate_limit', + target: { + type: 'second', + value: '10', + limit_by: 'ip', + }, + }, + { + name: 'set_custom_response', + target: { + status_code: 403, + content_type: 'text/plain', + content_body: 'Blocked', + }, + }, + ], + }, + ], + }, + }; + + const config = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = strategy.transformToConfig(manifest, config); + expect(result?.rules?.[0].behavior).toEqual({ + runFunction: { + path: 'function1', + }, + setWafRuleset: { + wafMode: 'learning', + wafId: '123', + }, + setRateLimit: { + type: 'second', + value: '10', + limitBy: 'ip', + }, + setCustomResponse: { + statusCode: 403, + contentType: 'text/plain', + contentBody: 'Blocked', + }, + }); + }); + + it('should handle empty behaviors array', () => { + const manifest = { + firewall: { + name: 'Test Firewall', + rules_engine: [ + { + name: 'Empty Behaviors Rule', + is_active: true, + behaviors: [], + }, + ], + }, + }; + + const config = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = strategy.transformToConfig(manifest, config); + expect(result?.rules?.[0].behavior).toEqual({}); + }); + + it('should handle unknown behavior types gracefully', () => { + const manifest = { + firewall: { + name: 'Test Firewall', + rules_engine: [ + { + name: 'Unknown Behavior Rule', + is_active: true, + behaviors: [ + { + name: 'unknown_behavior', + target: 'test', + }, + ], + }, + ], + }, + }; + + const config = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = strategy.transformToConfig(manifest, config); + expect(result?.rules?.[0].behavior).toEqual({}); + }); }); }); diff --git a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts index ae3a48bb..3c499192 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/secure/firewallProcessConfigStrategy.ts @@ -1,10 +1,6 @@ -import { - AzionConfig, - AzionEdgeFirewall, - AzionEdgeFirewallCriteriaWithValue, - AzionEdgeFirewallRule, -} from '../../../../types'; +import { AzionConfig, AzionFirewall, AzionFirewallCriteriaWithValue, AzionFirewallRule } from '../../../../types'; import ProcessConfigStrategy from '../../processConfigStrategy'; + /** * FirewallProcessConfigStrategy * @class FirewallProcessConfigStrategy @@ -12,58 +8,56 @@ import ProcessConfigStrategy from '../../processConfigStrategy'; */ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { transformToManifest(config: AzionConfig) { - const firewalls = config?.edgeFirewall; - if (!firewalls || firewalls.length === 0) { + const firewall = config?.firewall; + if (!firewall || Object.keys(firewall).length === 0) { return; } - return firewalls.map((firewall) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const payload: any = { - main_settings: { - name: firewall.name, - domains: firewall.domains || [], - is_active: firewall.active ?? true, - edge_functions_enabled: firewall.edgeFunctions ?? false, - network_protection_enabled: firewall.networkProtection ?? false, - waf_enabled: firewall.waf ?? false, - debug_rules: firewall.debugRules ?? false, - }, - }; - - if (firewall.rules && firewall.rules.length > 0) { - payload.rules_engine = firewall.rules.map((rule) => ({ - name: rule.name, - description: rule.description || '', - is_active: rule.active ?? true, - behaviors: this.transformBehaviorsToManifest(rule.behavior), - criteria: rule.criteria - ? [ - rule.criteria.map((criterion) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion as AzionEdgeFirewallCriteriaWithValue; - return { - ...rest, - variable: criterion.variable, - ...(isWithValue && { argument: argument }), - }; - }), - ] - : [ - [ - { - variable: rule.variable, - operator: 'matches', - conditional: 'if', - argument: rule.match, - }, - ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const payload: any = { + main_settings: { + name: firewall.name, + domains: firewall.domains || [], + is_active: firewall.active ?? true, + edge_functions_enabled: firewall.edgeFunctions ?? false, + network_protection_enabled: firewall.networkProtection ?? false, + waf_enabled: firewall.waf ?? false, + debug_rules: firewall.debugRules ?? false, + }, + }; + + if (firewall.rules && firewall.rules.length > 0) { + payload.rules_engine = firewall.rules.map((rule) => ({ + name: rule.name, + description: rule.description || '', + is_active: rule.active ?? true, + behaviors: this.transformBehaviorsToManifest(rule.behavior), + criteria: rule.criteria + ? [ + rule.criteria.map((criterion) => { + const isWithValue = 'inputValue' in criterion; + const { inputValue, ...rest } = criterion as AzionFirewallCriteriaWithValue; + return { + ...rest, + variable: criterion.variable, + ...(isWithValue && { input_value: inputValue }), + }; + }), + ] + : [ + [ + { + variable: rule.variable, + operator: 'matches', + conditional: 'if', + input_value: rule.match, + }, ], - })); - } + ], + })); + } - return payload; - }); + return payload; } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -129,49 +123,45 @@ class FirewallProcessConfigStrategy extends ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any transformToConfig(payload: any, transformedPayload: AzionConfig) { const firewall = payload.firewall; - if (!firewall || firewall.length === 0) { + if (!firewall || Object.keys(firewall).length === 0) { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformedPayload.edgeFirewall = firewall.map((firewallPayload: any) => { - const firewallConfig: AzionEdgeFirewall = { - name: firewallPayload.main_settings?.name, - domains: firewallPayload?.main_settings?.domains || [], - active: firewallPayload?.main_settings?.is_active ?? true, - edgeFunctions: firewallPayload?.main_settings?.edge_functions_enabled ?? false, - networkProtection: firewallPayload?.main_settings?.network_protection_enabled ?? false, - waf: firewallPayload?.main_settings?.waf_enabled ?? false, - debugRules: firewallPayload?.main_settings?.debug_rules ?? false, - }; - - if (firewallPayload.rules_engine && firewallPayload.rules_engine.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - firewallConfig.rules = firewallPayload.rules_engine.map((rule: any) => { - const firewallRule: AzionEdgeFirewallRule = { - name: rule.name, - description: rule.description || '', - active: rule.is_active ?? true, - behavior: this.transformBehaviorsToConfig(rule.behaviors), - criteria: - // eslint-disable-next-line @typescript-eslint/no-explicit-any - rule.criteria?.[0].map((criterion: any) => { - const isWithValue = 'argument' in criterion; - const { argument, ...rest } = criterion; - return { - ...rest, - ...(isWithValue && { argument: argument }), - }; - }) || [], - }; - return firewallRule; - }); - } - - return firewallConfig; - }); + const firewallConfig: AzionFirewall = { + name: firewall.main_settings?.name, + domains: firewall?.main_settings?.domains || [], + active: firewall?.main_settings?.is_active ?? true, + edgeFunctions: firewall?.main_settings?.edge_functions_enabled ?? false, + networkProtection: firewall?.main_settings?.network_protection_enabled ?? false, + waf: firewall?.main_settings?.waf_enabled ?? false, + debugRules: firewall?.main_settings?.debug_rules ?? false, + }; + + if (firewall.rules_engine && firewall.rules_engine.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + firewallConfig.rules = firewall.rules_engine.map((rule: any) => { + const firewallRule: AzionFirewallRule = { + name: rule.name, + description: rule.description || '', + active: rule.is_active ?? true, + behavior: this.transformBehaviorsToConfig(rule.behaviors), + criteria: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + rule.criteria?.[0].map((criterion: any) => { + const isWithValue = 'input_value' in criterion; + const { input_value, ...rest } = criterion; + return { + ...rest, + ...(isWithValue && { inputValue: input_value }), + }; + }) || [], + }; + return firewallRule; + }); + } - return transformedPayload.edgeFirewall; + transformedPayload.firewall = firewallConfig; + return transformedPayload.firewall; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts deleted file mode 100644 index 849a84b0..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { AzionConfig } from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; - -/** - * StorageProcessConfigStrategy - * @class StorageProcessConfigStrategy - * @description This class is implementation of the Storage ProcessConfig Strategy. - */ -class StorageProcessConfigStrategy extends ProcessConfigStrategy { - transformToManifest(config: AzionConfig) { - if (!Array.isArray(config?.edgeStorage) || config?.edgeStorage.length === 0) { - return; - } - - return config.edgeStorage.map((item) => ({ - name: item.name, - edge_access: item.edgeAccess || 'read_only', - dir: item.dir, - })); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: any, transformedPayload: AzionConfig) { - const storageConfig = payload.storage; - if (!Array.isArray(storageConfig) || storageConfig.length === 0) { - return; - } - - transformedPayload.edgeStorage = storageConfig.map((item) => ({ - name: item.name, - edgeAccess: item.edge_access || 'read_only', - dir: item.dir, - })); - - return transformedPayload.edgeStorage; - } -} - -export default StorageProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts deleted file mode 100644 index 01e5c650..00000000 --- a/packages/config/src/configProcessor/processStrategy/implementations/workloadProcessConfigStrategy.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { AzionConfig, AzionWorkload } from '../../../types'; -import ProcessConfigStrategy from '../processConfigStrategy'; - -class WorkloadProcessConfigStrategy extends ProcessConfigStrategy { - private validateApplicationReferences(config: AzionConfig, workloads: AzionWorkload[]) { - const applicationNames = config.edgeApplications?.map((app) => app.name) || []; - const firewallNames = config.edgeFirewall?.map((firewall) => firewall.name) || []; - - workloads.forEach((workload) => { - if (!applicationNames.includes(workload.edgeApplication)) { - throw new Error( - `Workload "${workload.name}" references non-existent Edge Application "${workload.edgeApplication}".`, - ); - } - }); - - workloads.forEach((workload) => { - if (workload.edgeFirewall && !firewallNames.includes(workload.edgeFirewall)) { - throw new Error( - `Workload "${workload.name}" references non-existent Edge Firewall "${workload.edgeFirewall}".`, - ); - } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any - transformToManifest(config: AzionConfig, context: Record) { - const workloads = config?.workloads; - if (!workloads || workloads.length === 0) return []; - - this.validateApplicationReferences(config, workloads); - - return workloads.map((workload: AzionWorkload) => ({ - name: workload.name, - alternate_domains: workload.alternateDomains || [], - edge_application: workload.edgeApplication, - active: workload.active ?? true, - network_map: workload.networkMap || '1', - edge_firewall: workload.edgeFirewall, - workload_hostname_allow_access: workload.workloadHostnameAllowAccess ?? true, - domains: workload.domains || [], - tls: { - certificate: workload.tls?.certificate || null, - ciphers: workload.tls?.ciphers || null, - minimum_version: workload.tls?.minimumVersion || 'tls_1_2', - }, - protocols: { - http: { - versions: workload.protocols?.http?.versions || ['http1', 'http2'], - http_ports: workload.protocols?.http?.httpPorts || [80], - https_ports: workload.protocols?.http?.httpsPorts || [443], - quic_ports: workload.protocols?.http?.quicPorts || null, - }, - }, - mtls: workload.mtls - ? { - verification: workload.mtls.verification || 'enforce', - certificate: workload.mtls.certificate, - crl: workload.mtls.crl, - } - : undefined, - })); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToConfig(payload: { workloads?: any[] }, transformedPayload: AzionConfig) { - if (!payload.workloads || payload.workloads.length === 0) { - return; - } - - transformedPayload.workloads = payload.workloads.map((workload) => ({ - name: workload.name, - alternateDomains: workload.alternate_domains, - edgeApplication: workload.edge_application, - active: workload.active, - networkMap: workload.network_map, - edgeFirewall: workload.edge_firewall, - workloadHostnameAllowAccess: workload.workload_hostname_allow_access, - domains: workload.domains, - tls: { - certificate: workload.tls?.certificate, - ciphers: workload.tls?.ciphers, - minimumVersion: workload.tls?.minimum_version, - }, - protocols: { - http: { - versions: workload.protocols?.http?.versions, - httpPorts: workload.protocols?.http?.http_ports, - httpsPorts: workload.protocols?.http?.https_ports, - quicPorts: workload.protocols?.http?.quic_ports, - }, - }, - mtls: workload.mtls - ? { - verification: workload.mtls.verification, - certificate: workload.mtls.certificate, - crl: workload.mtls.crl, - } - : undefined, - })); - - return transformedPayload.workloads; - } -} - -export default WorkloadProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/index.ts b/packages/config/src/configProcessor/processStrategy/index.ts index 1bad3ba4..b3fc7d93 100644 --- a/packages/config/src/configProcessor/processStrategy/index.ts +++ b/packages/config/src/configProcessor/processStrategy/index.ts @@ -1,27 +1,28 @@ -import EdgeApplicationProcessConfigStrategy from './implementations/application/edgeApplicationProcessConfigStrategy'; -import BuildProcessConfigStrategy from './implementations/buildProcessConfigStrategy'; -import EdgeConnectorProcessConfigStrategy from './implementations/edgeConnectorProcessConfigStrategy'; +import BuildProcessConfigStrategy from '../processStrategy/implementations/buildProcessConfigStrategy'; +import CacheProcessConfigStrategy from '../processStrategy/implementations/cacheProcessConfigStrategy'; +import DomainProcessConfigStrategy from '../processStrategy/implementations/domainProcessConfigStrategy'; +import OriginProcessConfigStrategy from '../processStrategy/implementations/originProcessConfigStrategy'; +import PurgeProcessConfigStrategy from '../processStrategy/implementations/purgeProcessConfigStrategy'; +import RulesProcessConfigStrategy from '../processStrategy/implementations/rulesProcessConfigStrategy'; +import NetworkListProcessConfigStrategy from '../processStrategy/implementations/secure/networkListProcessConfigStrategy'; +import WafProcessConfigStrategy from '../processStrategy/implementations/secure/wafProcessConfigStrategy'; +import ProcessConfigContext from '../processStrategy/processConfigContext'; import FunctionsProcessConfigStrategy from './implementations/functionsProcessConfigStrategy'; -import PurgeProcessConfigStrategy from './implementations/purgeProcessConfigStrategy'; import FirewallProcessConfigStrategy from './implementations/secure/firewallProcessConfigStrategy'; -import NetworkListProcessConfigStrategy from './implementations/secure/networkListProcessConfigStrategy'; -import WafProcessConfigStrategy from './implementations/secure/wafProcessConfigStrategy'; -import StorageProcessConfigStrategy from './implementations/storageProcessConfigStrategy'; -import WorkloadProcessConfigStrategy from './implementations/workloadProcessConfigStrategy'; -import ProcessConfigContext from './processConfigContext'; function factoryProcessContext() { const processConfigContext = new ProcessConfigContext(); processConfigContext.setStrategy('build', new BuildProcessConfigStrategy()); + processConfigContext.setStrategy('origin', new OriginProcessConfigStrategy()); + processConfigContext.setStrategy('cache', new CacheProcessConfigStrategy()); + processConfigContext.setStrategy('domain', new DomainProcessConfigStrategy()); processConfigContext.setStrategy('purge', new PurgeProcessConfigStrategy()); processConfigContext.setStrategy('networkList', new NetworkListProcessConfigStrategy()); processConfigContext.setStrategy('waf', new WafProcessConfigStrategy()); - processConfigContext.setStrategy('storage', new StorageProcessConfigStrategy()); processConfigContext.setStrategy('firewall', new FirewallProcessConfigStrategy()); - processConfigContext.setStrategy('edgeFunctions', new FunctionsProcessConfigStrategy()); - processConfigContext.setStrategy('edgeApplications', new EdgeApplicationProcessConfigStrategy()); - processConfigContext.setStrategy('workloads', new WorkloadProcessConfigStrategy()); - processConfigContext.setStrategy('edgeConnectors', new EdgeConnectorProcessConfigStrategy()); + processConfigContext.setStrategy('functions', new FunctionsProcessConfigStrategy()); + // Rules must be last to apply to behaviors (origin, cache...) + processConfigContext.setStrategy('rules', new RulesProcessConfigStrategy()); return processConfigContext; } diff --git a/packages/config/src/configProcessor/processStrategy/processConfigContext.ts b/packages/config/src/configProcessor/processStrategy/processConfigContext.ts index f15c42de..d6543db9 100644 --- a/packages/config/src/configProcessor/processStrategy/processConfigContext.ts +++ b/packages/config/src/configProcessor/processStrategy/processConfigContext.ts @@ -1,4 +1,4 @@ -import { AzionConfig } from '../../config/types'; +import { AzionConfig } from '../../types'; import ProcessConfigStrategy from './processConfigStrategy'; /** diff --git a/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts index 2eef7f26..d21360ff 100644 --- a/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/processConfigStrategy.ts @@ -4,11 +4,11 @@ * @description This class is the base class for all process config strategies. */ -import { AzionConfig } from '../../config/types'; +import { AzionConfig } from '../../types'; class ProcessConfigStrategy { // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformToManifest(config: any, context: any) { + transformToManifest(config: AzionConfig, context: any) { return context; } diff --git a/packages/config/src/configProcessor/validateConfig/index.test.ts b/packages/config/src/configProcessor/validateConfig/index.test.ts index 814c72e6..b51259ec 100644 --- a/packages/config/src/configProcessor/validateConfig/index.test.ts +++ b/packages/config/src/configProcessor/validateConfig/index.test.ts @@ -8,6 +8,9 @@ describe('generate', () => { build: { preset: 'next', polyfills: true, + custom: { + minify: true, + }, }, }; expect(() => validateConfig(config)).not.toThrow(); diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts index 8ff50ee8..fdd5c72a 100644 --- a/packages/config/src/constants.ts +++ b/packages/config/src/constants.ts @@ -257,46 +257,9 @@ export const RULE_BEHAVIOR_NAMES = [ 'run_function', 'set_cache_policy', 'set_cookie', - 'set_edge_connector', + 'set_origin', ] as const; // Tipos para Rules export type RulePhase = (typeof RULE_PHASES)[number]; export type RuleBehaviorName = (typeof RULE_BEHAVIOR_NAMES)[number]; - -// Workload Constants -export const WORKLOAD_NETWORK_MAP = ['1', '2'] as const; - -export const WORKLOAD_TLS_CIPHERS = ['TLSv1.2_2018', 'TLSv1.2_2019', 'TLSv1.3_2022', 'TLSv1.2_2021'] as const; - -export const WORKLOAD_TLS_VERSIONS = ['', 'tls_1_0', 'tls_1_1', 'tls_1_2', 'tls_1_3'] as const; - -export const WORKLOAD_MTLS_VERIFICATION = ['enforce', 'permissive'] as const; - -export const WORKLOAD_HTTP_VERSIONS = ['http1', 'http2'] as const; - -// Tipos para Workload -export type WorkloadNetworkMap = (typeof WORKLOAD_NETWORK_MAP)[number]; -export type WorkloadTLSCipher = (typeof WORKLOAD_TLS_CIPHERS)[number]; -export type WorkloadTLSVersion = (typeof WORKLOAD_TLS_VERSIONS)[number]; -export type WorkloadMTLSVerification = (typeof WORKLOAD_MTLS_VERIFICATION)[number]; -export type WorkloadHTTPVersion = (typeof WORKLOAD_HTTP_VERSIONS)[number]; - -export const EDGE_CONNECTOR_TYPES = ['http', 's3', 'edge_storage', 'live_ingest'] as const; -export const EDGE_CONNECTOR_LOAD_BALANCE = ['off', 'round_robin', 'ip_hash', 'least_conn'] as const; -export const EDGE_CONNECTOR_SERVER_ROLE = ['primary', 'backup'] as const; -export const EDGE_CONNECTOR_CONNECTION_PREFERENCE = ['IPv4', 'IPv6'] as const; - -export const EDGE_CONNECTOR_REQUIRED_PROPERTIES = { - http: ['versions', 'host', 'path'], - live_ingest: ['endpoint'], - s3: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], - edge_storage: ['bucket'], -} as const; - -export const EDGE_CONNECTOR_ALLOWED_PROPERTIES = { - http: ['versions', 'host', 'path', 'followingRedirect', 'realIpHeader', 'realPortHeader'], - live_ingest: ['endpoint'], - s3: ['host', 'bucket', 'path', 'region', 'accessKey', 'secretKey'], - edge_storage: ['bucket', 'prefix'], -} as const; diff --git a/packages/config/src/rules/constants.ts b/packages/config/src/rules/constants.ts deleted file mode 100644 index d4bae5cb..00000000 --- a/packages/config/src/rules/constants.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Common file extensions used in web applications - */ -export const FILE_EXTENSIONS = { - // Images - IMAGES: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'] as const, - - // Fonts - FONTS: ['ttf', 'otf', 'woff', 'woff2', 'eot'] as const, - - // Documents - DOCUMENTS: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'] as const, - - // Media - MEDIA: ['mp4', 'webm', 'mp3', 'wav', 'ogg'] as const, - - // Code & Data - CODE_AND_DATA: ['css', 'js', 'json', 'xml', 'html', 'txt', 'csv'] as const, - - // Archives - ARCHIVES: ['zip', 'rar', '7z', 'tar', 'gz'] as const, - - // Other - OTHER: ['webmanifest', 'map', 'md', 'yaml', 'yml'] as const, -} as const; - -/** - * All file extensions combined - */ -export const ALL_EXTENSIONS = [ - ...FILE_EXTENSIONS.IMAGES, - ...FILE_EXTENSIONS.FONTS, - ...FILE_EXTENSIONS.DOCUMENTS, - ...FILE_EXTENSIONS.MEDIA, - ...FILE_EXTENSIONS.CODE_AND_DATA, - ...FILE_EXTENSIONS.ARCHIVES, - ...FILE_EXTENSIONS.OTHER, -] as const; diff --git a/packages/config/src/rules/index.ts b/packages/config/src/rules/index.ts deleted file mode 100644 index fc9a5110..00000000 --- a/packages/config/src/rules/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './request/createMPA'; -export * from './request/createSPA'; diff --git a/packages/config/src/rules/request/createMPA.ts b/packages/config/src/rules/request/createMPA.ts deleted file mode 100644 index 8e41439b..00000000 --- a/packages/config/src/rules/request/createMPA.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { AzionRules } from 'azion/config'; -import { ALL_EXTENSIONS } from '../constants'; - -/** - * Creates rules for a Multi Page Application (MPA) on Azion Edge Platform. - * This configuration is optimized for static site hosting with proper routing and asset delivery. - * - * Features: - * - Static asset delivery with caching - * - Proper handling of directory and subpath routing - * - Automatic index.html handling for clean URLs - * - * @param options Configuration options for the MPA rules - * @param options.bucket The name of the edge storage bucket to use - * @param options.staticExtensions List of file extensions to be treated as static assets - * @returns Array of rules configured for MPA hosting on Azion Edge - */ -export function createMPARules( - options: { - edgeConnector?: string; - staticExtensions?: string[]; - } = {}, -): AzionRules { - const { edgeConnector = 'edge-connector', staticExtensions = ALL_EXTENSIONS } = options; - - return { - request: [ - { - name: 'Deliver Static Assets', - match: `\\.(${staticExtensions.join('|')})$`, - behavior: { - setEdgeConnector: edgeConnector, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '.*/$', - behavior: { - setEdgeConnector: edgeConnector, - rewrite: '${uri}index.html', - }, - }, - { - name: 'Redirect to index.html for Subpaths', - match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', - behavior: { - setEdgeConnector: edgeConnector, - rewrite: '${uri}/index.html', - }, - }, - ], - response: [], - }; -} diff --git a/packages/config/src/rules/request/createSPA.ts b/packages/config/src/rules/request/createSPA.ts deleted file mode 100644 index f10d97f2..00000000 --- a/packages/config/src/rules/request/createSPA.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { AzionRules } from 'azion/config'; -import { ALL_EXTENSIONS } from '../constants'; - -/** - * Creates rules for a Single Page Application (SPA) on Azion Edge Platform. - * This configuration is optimized for SPA hosting with proper routing and asset delivery. - * - * Features: - * - Static asset delivery with caching - * - Client-side routing support - * - Automatic fallback to index.html for all routes - * - * @param options Configuration options for the SPA rules - * @param options.bucket The name of the edge storage bucket to use - * @param options.staticExtensions List of file extensions to be treated as static assets - * @returns Array of rules configured for SPA hosting on Azion Edge - */ -export function createSPARules( - options: { - edgeConnector?: string; - staticExtensions?: string[]; - } = {}, -): AzionRules { - const { edgeConnector = 'edge-connector', staticExtensions = ALL_EXTENSIONS } = options; - - return { - request: [ - { - name: 'Deliver Static Assets', - match: `\\.(${staticExtensions.join('|')})$`, - behavior: { - setEdgeConnector: edgeConnector, - deliver: true, - }, - }, - { - name: 'Redirect to index.html', - match: '^\\/', - behavior: { - setEdgeConnector: edgeConnector, - rewrite: '/index.html', - }, - }, - ], - response: [], - }; -} diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 6ffd53e1..9095200c 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -1,7 +1,4 @@ import { - EDGE_CONNECTOR_CONNECTION_PREFERENCE, - EDGE_CONNECTOR_LOAD_BALANCE, - EDGE_CONNECTOR_TYPES, FirewallRateLimitBy, FirewallRateLimitType, FirewallWafMode, @@ -12,11 +9,6 @@ import { RuleVariable, WafMode, WafSensitivity, - WorkloadHTTPVersion, - WorkloadMTLSVerification, - WorkloadNetworkMap, - WorkloadTLSCipher, - WorkloadTLSVersion, } from './constants'; import { FetchEvent } from 'azion/types'; @@ -55,6 +47,56 @@ export type AzionDomain = { }; }; +/** + * Origin configuration for Azion. + */ +export type AzionOrigin = { + /** Origin ID */ + id?: number; + /** Origin key */ + key?: string; + /** Origin name */ + name: string; + /** Origin type */ + type: string; + /** Bucket name for S3-like origins */ + bucket?: string | null; + /** Prefix for S3-like origins */ + prefix?: string | null; + /** Addresses for the origin */ + addresses?: + | string[] + | { + /** Address of the origin */ + address: string; + /** Weight for load balancing */ + weight?: number; + }[]; + /** Host header to be sent to the origin */ + hostHeader?: string; + /** Protocol policy for communicating with the origin */ + protocolPolicy?: 'http' | 'https' | 'preserve'; + /** Indicates if redirection should be used */ + redirection?: boolean; + /** Load balancing method */ + method?: 'ip_hash' | 'least_connections' | 'round_robin'; + /** Path to be appended to the origin address */ + path?: string; + /** Connection timeout in seconds */ + connectionTimeout?: number; + /** Timeout between bytes in seconds */ + timeoutBetweenBytes?: number; + /** HMAC authentication configuration */ + hmac?: { + /** AWS region */ + region: string; + /** AWS access key */ + accessKey: string; + /** AWS secret key */ + secretKey: string; + }; +}; + /** * Cache configuration for Azion. */ @@ -109,7 +151,7 @@ export type AzionRuleCriteriaWithValue = AzionRuleCriteriaBase & { /** Operator for comparison that requires input value */ operator: RuleOperatorWithValue; /** Input value for comparison */ - argument: string; + inputValue: string; }; export type AzionRuleCriteriaWithoutValue = AzionRuleCriteriaBase & { @@ -137,8 +179,17 @@ export type AzionRequestRule = { criteria?: AzionRuleCriteria[]; /** Behavior to be applied when the rule matches */ behavior?: { + /** Set a new origin */ + setOrigin?: { + /** Origin name */ + name: string; + /** Origin type */ + type: string; + }; /** Rewrite the request */ rewrite?: string; + /** Set headers */ + setHeaders?: string[]; /** Bypass cache */ bypassCache?: boolean | null; /** Force HTTPS */ @@ -187,16 +238,6 @@ export type AzionRequestRule = { /** CDN cache TTL */ cdn_cache_settings_maximum_ttl?: number | null; }; - /** Finish request phase */ - finishRequestPhase?: boolean; - /** Set Edge connector */ - setEdgeConnector?: string; - /** Add request header */ - addRequestHeader?: string[]; - /** Add request cookie */ - addRequestCookie?: string; - /** Filter request cookie */ - filterRequestCookie?: string; }; }; @@ -218,6 +259,8 @@ export type AzionResponseRule = { criteria?: AzionRuleCriteria[]; /** Behavior to be applied when the rule matches */ behavior?: { + /** Set a cookie */ + setCookie?: string | null; /** Set headers */ setHeaders?: string[]; /** Deliver the content */ @@ -233,6 +276,8 @@ export type AzionResponseRule = { }; /** Enable GZIP compression */ enableGZIP?: boolean | null; + /** Filter a cookie */ + filterCookie?: string | null; /** Filter a header */ filterHeader?: string | null; /** Run a serverless function */ @@ -241,10 +286,6 @@ export type AzionResponseRule = { redirectTo301?: string | null; /** Redirect with 302 status */ redirectTo302?: string | null; - /** Add response header */ - addResponseHeader?: string[]; - /** Filter response cookie */ - filterResponseCookie?: string; }; }; @@ -261,12 +302,14 @@ export type AzionRules = { * Purge configuration for Azion. */ export type AzionPurge = { - /** Items to be purged */ - items: string[]; - /** Cache layer to be purged */ - layer?: 'edge_cache' | 'tiered_cache'; /** Purge type */ type: 'url' | 'cachekey' | 'wildcard'; + /** URLs to be purged */ + urls: string[]; + /** HTTP method for purge request */ + method?: 'delete'; + /** Cache layer to be purged */ + layer?: 'edge_caching' | 'l2_caching'; }; export type PresetInput = string | AzionBuildPreset; @@ -298,105 +341,48 @@ export type AzionNetworkList = { listContent: string[] | number[]; }; -/** - * Storage binding configuration for Azion. - */ -export type AzionStorageBinding = { - /** Storage bucket name */ - bucket: string; - /** Storage prefix */ - prefix: string; -}; - -/** - * Bindings configuration for Azion. - */ -export type AzionBindings = { - /** Storage bindings */ - storage?: AzionStorageBinding; -}; - /** * Function configuration for Azion. */ -export type AzionEdgeFunction = { +export type AzionFunction = { /** Function name */ name: string; /** Function path */ path: string; /** Optional arguments to be passed to the function */ args?: Record; - /** Function bindings */ - bindings?: AzionBindings; -}; - -/** - * Storage configuration for Azion. - */ -export type AzionBucket = { - /** Storage name */ - name: string; - /** Edge access type */ - edgeAccess?: 'read_only' | 'read_write' | 'restricted'; - /** Storage path */ - dir: string; }; -/** - * Edge Application configuration for Azion. - */ -export type AzionEdgeApplication = { - /** Application name */ - name: string; - /** Enable edge cache */ - edgeCacheEnabled?: boolean; - /** Enable edge functions */ - edgeFunctionsEnabled?: boolean; - /** Enable application accelerator */ - applicationAcceleratorEnabled?: boolean; - /** Enable image processor */ - imageProcessorEnabled?: boolean; - /** Enable tiered cache */ - tieredCacheEnabled?: boolean; - /** Indicates if the application is active */ - active?: boolean; - /** Enable debug mode */ - debug?: boolean; - /** Cache settings */ - cache?: AzionCache[]; - /** Rules configuration */ - rules?: AzionRules; -}; /** * Main configuration type for Azion. */ export type AzionConfig = { /** Build configuration */ build?: AzionBuild; - /** Edge Application configuration */ - edgeApplications?: AzionEdgeApplication[]; + /** Domain configuration */ + domain?: AzionDomain; + /** Origin configurations */ + origin?: AzionOrigin[]; + /** Cache configurations */ + cache?: AzionCache[]; /** Functions configurations */ - edgeFunctions?: AzionEdgeFunction[]; - /** Edge Connectors configuration */ - edgeConnectors?: AzionEdgeConnector[]; - /** Storage configurations */ - edgeStorage?: AzionBucket[]; + functions?: AzionFunction[]; + /** Rules configuration */ + rules?: AzionRules; + /** Purge configurations */ + purge?: AzionPurge[]; /** Firewall configuration */ - edgeFirewall?: AzionEdgeFirewall[]; + firewall?: AzionFirewall; /** Network list configurations */ networkList?: AzionNetworkList[]; - /** Purge configurations */ - purge?: AzionPurge[]; /** WAF configuration */ waf?: AzionWaf[]; - /** Workload configuration */ - workloads?: AzionWorkload[]; }; /** * Firewall behavior configuration for Azion. */ -export type AzionEdgeFirewallBehavior = { +export type AzionFirewallBehavior = { /** Run a serverless function */ runFunction?: string; /** Set WAF ruleset */ @@ -432,31 +418,31 @@ export type AzionEdgeFirewallBehavior = { }; }; -export type AzionEdgeFirewallCriteriaBase = { +export type AzionFirewallCriteriaBase = { /** Variable to be evaluated */ variable: RuleVariable; /** Conditional type */ conditional: RuleConditional; }; -export type AzionEdgeFirewallCriteriaWithValue = AzionEdgeFirewallCriteriaBase & { +export type AzionFirewallCriteriaWithValue = AzionFirewallCriteriaBase & { /** Operator for comparison that requires input value */ operator: RuleOperatorWithValue; /** Input value for comparison */ inputValue: string; }; -export type AzionEdgeFirewallCriteriaWithoutValue = AzionEdgeFirewallCriteriaBase & { +export type AzionFirewallCriteriaWithoutValue = AzionFirewallCriteriaBase & { /** Operator for comparison that doesn't require input value */ operator: RuleOperatorWithoutValue; }; -export type AzionEdgeFirewallCriteria = AzionEdgeFirewallCriteriaWithValue | AzionEdgeFirewallCriteriaWithoutValue; +export type AzionFirewallCriteria = AzionFirewallCriteriaWithValue | AzionFirewallCriteriaWithoutValue; /** * Firewall rule configuration for Azion. */ -export type AzionEdgeFirewallRule = { +export type AzionFirewallRule = { /** Rule name */ name: string; /** Rule description */ @@ -468,15 +454,15 @@ export type AzionEdgeFirewallRule = { /** Variable to be used in the match */ variable?: RuleVariable; /** Array of criteria for complex conditions */ - criteria?: AzionEdgeFirewallCriteria[]; + criteria?: AzionFirewallCriteria[]; /** Behavior to be applied when the rule matches */ - behavior: AzionEdgeFirewallBehavior; + behavior: AzionFirewallBehavior; }; /** * Firewall configuration for Azion. */ -export type AzionEdgeFirewall = { +export type AzionFirewall = { /** Firewall name */ name: string; /** List of domains */ @@ -492,7 +478,7 @@ export type AzionEdgeFirewall = { /** Variable to be used in the match */ variable?: RuleVariable; /** List of firewall rules */ - rules?: AzionEdgeFirewallRule[]; + rules?: AzionFirewallRule[]; /** Debug mode */ debugRules?: boolean; }; @@ -602,114 +588,3 @@ export interface AzionPrebuildResult { export interface AzionConfigs { configs: AzionConfig[]; } - -export type AzionWorkloadTLS = { - certificate?: number | null; - ciphers?: WorkloadTLSCipher | null; - minimumVersion?: WorkloadTLSVersion | null; -}; - -export type AzionWorkloadProtocols = { - http: { - versions: WorkloadHTTPVersion[]; - httpPorts: number[]; - httpsPorts: number[]; - quicPorts?: number[] | null; - }; -}; - -export type AzionWorkloadMTLS = { - verification: WorkloadMTLSVerification; - certificate?: number | null; - crl?: number[] | null; -}; - -export type AzionWorkload = { - name: string; - alternateDomains?: string[]; - edgeApplication: string; - active?: boolean; - networkMap?: WorkloadNetworkMap; - edgeFirewall?: string | null; - tls?: AzionWorkloadTLS; - protocols?: AzionWorkloadProtocols; - mtls?: AzionWorkloadMTLS; - domains: string[]; - workloadHostnameAllowAccess?: boolean; -}; - -export type EdgeConnectorType = (typeof EDGE_CONNECTOR_TYPES)[number]; -export type EdgeConnectorLoadBalance = (typeof EDGE_CONNECTOR_LOAD_BALANCE)[number]; -export type EdgeConnectorConnectionPreference = (typeof EDGE_CONNECTOR_CONNECTION_PREFERENCE)[number]; - -export interface EdgeConnectorModules { - loadBalancerEnabled: boolean; - originShieldEnabled: boolean; -} - -export interface EdgeConnectorTLS { - policy: string; -} - -export interface EdgeConnectorAddress { - address: string; - plainPort?: number; - tlsPort?: number; - serverRole?: 'primary' | 'backup'; - weight?: number; - active?: boolean; - maxConns?: number; - maxFails?: number; - failTimeout?: number; - tls?: { - policy: 'off' | 'on' | 'preserve'; - }; -} - -export interface HttpTypeProperty { - versions: string[]; - host: string; - path: string; - followingRedirect?: boolean; - realIpHeader?: string; - realPortHeader?: string; -} - -export interface LiveIngestTypeProperty { - endpoint: string; -} - -export interface S3TypeProperty { - host: string; - bucket: string; - path: string; - region: string; - accessKey: string; - secretKey: string; -} - -export interface StorageTypeProperty { - bucket: string; - prefix?: string; -} - -export type EdgeConnectorTypeProperty = - | HttpTypeProperty - | LiveIngestTypeProperty - | S3TypeProperty - | StorageTypeProperty; - -export interface AzionEdgeConnector { - name: string; - modules: EdgeConnectorModules; - active?: boolean; - type: EdgeConnectorType; - typeProperties: EdgeConnectorTypeProperty; - addresses?: EdgeConnectorAddress[]; - tls?: EdgeConnectorTLS; - loadBalanceMethod?: EdgeConnectorLoadBalance; - connectionPreference?: EdgeConnectorConnectionPreference[]; - connectionTimeout?: number; - readWriteTimeout?: number; - maxRetries?: number; -} diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json index 12a498a3..18fe1ee3 100644 --- a/packages/config/tsconfig.json +++ b/packages/config/tsconfig.json @@ -13,8 +13,7 @@ "baseUrl": ".", "paths": { "azion/types": ["../types/src/index.ts"], - "azion/bundler": ["../bundler/src/index.ts"], - "azion/config": ["../config/src/index.ts"] + "azion/bundler": ["../bundler/src/index.ts"] } } } diff --git a/packages/config/tsup.config.ts b/packages/config/tsup.config.ts deleted file mode 100644 index c84b69e2..00000000 --- a/packages/config/tsup.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from 'tsup'; - -export default defineConfig({ - entry: ['src/index.ts', 'src/rules/index.ts'], - format: ['cjs', 'esm'], - splitting: true, - sourcemap: false, - clean: true, - bundle: true, - dts: true, - minify: true, - minifyWhitespace: true, -}); diff --git a/packages/presets/src/presets/angular/config.ts b/packages/presets/src/presets/angular/config.ts index bf92514a..01cf1026 100644 --- a/packages/presets/src/presets/angular/config.ts +++ b/packages/presets/src/presets/angular/config.ts @@ -1,38 +1,50 @@ -import type { AzionConfig } from 'azion/config'; -import { createSPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +const config = defineConfig({ build: { bundler: 'esbuild', + preset: 'angular', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createSPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); export default config; diff --git a/packages/presets/src/presets/astro/config.ts b/packages/presets/src/presets/astro/config.ts index 873fec70..f4c5244e 100644 --- a/packages/presets/src/presets/astro/config.ts +++ b/packages/presets/src/presets/astro/config.ts @@ -1,38 +1,56 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +const config = defineConfig({ build: { bundler: 'esbuild', + preset: 'astro', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); export default config; diff --git a/packages/presets/src/presets/docusaurus/config.ts b/packages/presets/src/presets/docusaurus/config.ts index 873fec70..03af6e00 100644 --- a/packages/presets/src/presets/docusaurus/config.ts +++ b/packages/presets/src/presets/docusaurus/config.ts @@ -1,38 +1,56 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +const config = defineConfig({ build: { bundler: 'esbuild', + preset: 'docusaurus', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); export default config; diff --git a/packages/presets/src/presets/eleventy/config.ts b/packages/presets/src/presets/eleventy/config.ts index 873fec70..6af8bd85 100644 --- a/packages/presets/src/presets/eleventy/config.ts +++ b/packages/presets/src/presets/eleventy/config.ts @@ -1,38 +1,56 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +const config = defineConfig({ build: { bundler: 'esbuild', + preset: 'eleventy', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); export default config; diff --git a/packages/presets/src/presets/emscripten/config.ts b/packages/presets/src/presets/emscripten/config.ts index 592799b4..370859b9 100644 --- a/packages/presets/src/presets/emscripten/config.ts +++ b/packages/presets/src/presets/emscripten/config.ts @@ -1,10 +1,10 @@ -import type { AzionBuild, AzionConfig } from 'azion/config'; +import { AzionBuild, defineConfig } from 'azion/config'; import webpack, { Configuration } from 'webpack'; -const config: AzionConfig = { +const config = defineConfig({ build: { - entry: 'handler.js', - bundler: 'webpack', + entry: 'handler.ts', + bundler: 'esbuild', polyfills: false, extend: (context: Configuration) => { context = { @@ -33,28 +33,23 @@ const config: AzionConfig = { return context; }, } as AzionBuild, - edgeFunctions: [ + functions: [ { - name: '$EDGE_FUNCTION_NAME', - path: '$LOCAL_FUNCTION_PATH', + name: 'my-emscripten-function', + path: '.edge/functions/handler.js', }, ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: '$EDGE_FUNCTION_NAME', - }, - }, - ], + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: 'my-emscripten-function', + }, }, - }, - ], -}; + ], + }, +}); export default config; diff --git a/packages/presets/src/presets/gatsby/config.ts b/packages/presets/src/presets/gatsby/config.ts index 873fec70..25997f6d 100644 --- a/packages/presets/src/presets/gatsby/config.ts +++ b/packages/presets/src/presets/gatsby/config.ts @@ -1,38 +1,54 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'gatsby', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/hexo/config.ts b/packages/presets/src/presets/hexo/config.ts index 873fec70..02311de9 100644 --- a/packages/presets/src/presets/hexo/config.ts +++ b/packages/presets/src/presets/hexo/config.ts @@ -1,38 +1,54 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'hexo', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/html/config.ts b/packages/presets/src/presets/html/config.ts index 873fec70..00f79162 100644 --- a/packages/presets/src/presets/html/config.ts +++ b/packages/presets/src/presets/html/config.ts @@ -1,38 +1,29 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'html', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, - }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + ], + }, +}); diff --git a/packages/presets/src/presets/hugo/config.ts b/packages/presets/src/presets/hugo/config.ts index 873fec70..cb463046 100644 --- a/packages/presets/src/presets/hugo/config.ts +++ b/packages/presets/src/presets/hugo/config.ts @@ -1,38 +1,54 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'hugo', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/javascript/config.ts b/packages/presets/src/presets/javascript/config.ts index def01da7..23288501 100644 --- a/packages/presets/src/presets/javascript/config.ts +++ b/packages/presets/src/presets/javascript/config.ts @@ -1,30 +1,26 @@ -import type { AzionConfig } from 'azion/config'; -const config: AzionConfig = { +import { defineConfig } from 'azion/config'; + +export default defineConfig({ build: { - entry: 'handler.js', + entry: 'handler.ts', + preset: 'javascript', + polyfills: true, }, - edgeFunctions: [ + functions: [ { - name: '$EDGE_FUNCTION_NAME', - path: '$LOCAL_FUNCTION_PATH', + name: 'my-javascript-function', + path: '.edge/functions/handler.js', }, ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: '$EDGE_FUNCTION_NAME', - }, - }, - ], + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: 'my-javascript-function', + }, }, - }, - ], -}; - -export default config; + ], + }, +}); diff --git a/packages/presets/src/presets/jekyll/config.ts b/packages/presets/src/presets/jekyll/config.ts index 873fec70..76bdbe91 100644 --- a/packages/presets/src/presets/jekyll/config.ts +++ b/packages/presets/src/presets/jekyll/config.ts @@ -1,38 +1,54 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'jekyll', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/next/config.ts b/packages/presets/src/presets/next/config.ts index 390f9199..4b63f86b 100644 --- a/packages/presets/src/presets/next/config.ts +++ b/packages/presets/src/presets/next/config.ts @@ -1,68 +1,55 @@ -import type { AzionConfig } from 'azion/config'; -const config: AzionConfig = { +import { defineConfig } from 'azion/config'; + +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'next', polyfills: true, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ + functions: [ { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, - }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', - }, + name: 'handler', + path: '.edge/functions/handler.js', }, ], - edgeFunctions: [ - { - name: '$EDGE_FUNCTION_NAME', - path: '$LOCAL_FUNCTION_PATH', - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: { - request: [ - { - name: 'Next.js Static Assets', - match: '^\\/_next\\/static\\/', - behavior: { - setEdgeConnector: '$EDGE_CONNECTOR_NAME', - deliver: true, - }, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/_next\\/static\\/', // starts with '/_next/static/' + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', }, - { - name: 'Deliver Static Assets', - match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', - behavior: { - setEdgeConnector: '$EDGE_CONNECTOR_NAME', - deliver: true, - }, - }, - { - name: 'Execute Next.js Function', - match: '^\\/', - behavior: { - runFunction: '$EDGE_FUNCTION_NAME', - forwardCookies: true, - }, + deliver: true, + }, + }, + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', }, - ], + deliver: true, + }, }, - }, - ], -}; - -export default config; + { + name: 'Execute Edge Function', + match: '^/', + behavior: { + runFunction: 'handler', + forwardCookies: true, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/nuxt/config.ts b/packages/presets/src/presets/nuxt/config.ts index 873fec70..bb01b0d9 100644 --- a/packages/presets/src/presets/nuxt/config.ts +++ b/packages/presets/src/presets/nuxt/config.ts @@ -1,38 +1,56 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'nuxt', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + // eslint-disable-next-line no-template-curly-in-string + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + // eslint-disable-next-line no-template-curly-in-string + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/preact/config.ts b/packages/presets/src/presets/preact/config.ts index 873fec70..5b9552f7 100644 --- a/packages/presets/src/presets/preact/config.ts +++ b/packages/presets/src/presets/preact/config.ts @@ -1,38 +1,46 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; - -const config: AzionConfig = { +import { defineConfig } from 'azion/config'; +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'preact', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/qwik/config.ts b/packages/presets/src/presets/qwik/config.ts index 873fec70..fa38186f 100644 --- a/packages/presets/src/presets/qwik/config.ts +++ b/packages/presets/src/presets/qwik/config.ts @@ -1,38 +1,47 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'qwik', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/react/config.ts b/packages/presets/src/presets/react/config.ts index bf92514a..1230a6d0 100644 --- a/packages/presets/src/presets/react/config.ts +++ b/packages/presets/src/presets/react/config.ts @@ -1,38 +1,49 @@ -import type { AzionConfig } from 'azion/config'; -import { createSPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'react', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createSPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/rustwasm/config.ts b/packages/presets/src/presets/rustwasm/config.ts index d354c9d5..47678870 100644 --- a/packages/presets/src/presets/rustwasm/config.ts +++ b/packages/presets/src/presets/rustwasm/config.ts @@ -1,11 +1,11 @@ -import type { AzionBuild, AzionConfig } from 'azion/config'; +import { AzionBuild, defineConfig } from 'azion/config'; import webpack, { Configuration } from 'webpack'; -const config: AzionConfig = { +export default defineConfig({ build: { - entry: 'handler.js', + entry: 'handler.ts', + preset: 'rustwasm', polyfills: false, - bundler: 'webpack', extend: (context: Configuration) => { context = { ...context, @@ -33,28 +33,21 @@ const config: AzionConfig = { return context; }, } as AzionBuild, - edgeFunctions: [ + functions: [ { - name: '$EDGE_FUNCTION_NAME', - path: '$LOCAL_FUNCTION_PATH', + name: 'my-rustwasm-function', + path: '.edge/functions/handler.js', }, ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: '$EDGE_FUNCTION_NAME', - }, - }, - ], + rules: { + request: [ + { + name: 'Execute Edge F nction', + match: '^\\/', + behavior: { + runFunction: 'my-rustwasm-function', + }, }, - }, - ], -}; - -export default config; + ], + }, +}); diff --git a/packages/presets/src/presets/stencil/config.ts b/packages/presets/src/presets/stencil/config.ts index bf92514a..9835a9d4 100644 --- a/packages/presets/src/presets/stencil/config.ts +++ b/packages/presets/src/presets/stencil/config.ts @@ -1,38 +1,47 @@ -import type { AzionConfig } from 'azion/config'; -import { createSPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { - bundler: 'esbuild', + preset: 'stencil', }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createSPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/svelte/config.ts b/packages/presets/src/presets/svelte/config.ts index 873fec70..795b8c08 100644 --- a/packages/presets/src/presets/svelte/config.ts +++ b/packages/presets/src/presets/svelte/config.ts @@ -1,38 +1,56 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'svelte', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + // eslint-disable-next-line no-template-curly-in-string + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + // eslint-disable-next-line no-template-curly-in-string + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/typescript/config.ts b/packages/presets/src/presets/typescript/config.ts index 686cff8c..675f425e 100644 --- a/packages/presets/src/presets/typescript/config.ts +++ b/packages/presets/src/presets/typescript/config.ts @@ -1,30 +1,26 @@ -import type { AzionConfig } from 'azion/config'; -const config: AzionConfig = { +import { defineConfig } from 'azion/config'; + +export default defineConfig({ build: { entry: 'handler.ts', + preset: 'typescript', + polyfills: true, }, - edgeFunctions: [ + functions: [ { - name: '$EDGE_FUNCTION_NAME', - path: '$LOCAL_FUNCTION_PATH', + name: 'my-typescript-function', + path: '.edge/functions/handler.js', }, ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: { - request: [ - { - name: 'Execute Edge Function', - match: '^\\/', - behavior: { - runFunction: '$EDGE_FUNCTION_NAME', - }, - }, - ], + rules: { + request: [ + { + name: 'Execute Edge Function', + match: '^\\/', + behavior: { + runFunction: 'my-typescript-function', + }, }, - }, - ], -}; - -export default config; + ], + }, +}); diff --git a/packages/presets/src/presets/vitepress/config.ts b/packages/presets/src/presets/vitepress/config.ts index 873fec70..c23405b3 100644 --- a/packages/presets/src/presets/vitepress/config.ts +++ b/packages/presets/src/presets/vitepress/config.ts @@ -1,38 +1,54 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'vitepress', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; - -export default config; + { + name: 'Redirect to index.html', + match: '.*/$', + behavior: { + rewrite: '${uri}index.html', + }, + }, + { + name: 'Redirect to index.html for Subpaths', + match: '^(?!.*\\/$)(?![\\s\\S]*\\.[a-zA-Z0-9]+$).*', + behavior: { + rewrite: '${uri}/index.html', + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/vue/config.ts b/packages/presets/src/presets/vue/config.ts index bf92514a..22ef8bd8 100644 --- a/packages/presets/src/presets/vue/config.ts +++ b/packages/presets/src/presets/vue/config.ts @@ -1,38 +1,48 @@ -import type { AzionConfig } from 'azion/config'; -import { createSPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { + preset: 'vue', bundler: 'esbuild', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createSPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/src/presets/vuepress/config.ts b/packages/presets/src/presets/vuepress/config.ts index 873fec70..33e710af 100644 --- a/packages/presets/src/presets/vuepress/config.ts +++ b/packages/presets/src/presets/vuepress/config.ts @@ -1,38 +1,48 @@ -import type { AzionConfig } from 'azion/config'; -import { createMPARules } from 'azion/config/rules'; +import { defineConfig } from 'azion/config'; -const config: AzionConfig = { +export default defineConfig({ build: { bundler: 'esbuild', + preset: 'vuepress', + polyfills: false, }, - edgeStorage: [ + origin: [ { - name: '$BUCKET_NAME', - dir: '$LOCAL_BUCKET_DIR', - edgeAccess: 'read_only', + name: 'origin-storage-default', + type: 'object_storage', }, ], - edgeConnectors: [ - { - name: '$EDGE_CONNECTOR_NAME', - modules: { - loadBalancerEnabled: false, - originShieldEnabled: false, + rules: { + request: [ + { + name: 'Set Storage Origin for All Requests', + match: '^\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + }, }, - type: 'edge_storage', - typeProperties: { - bucket: '$BUCKET_NAME', + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json|xml|html)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, }, - }, - ], - edgeApplications: [ - { - name: '$EDGE_APPLICATION_NAME', - rules: createMPARules({ - edgeConnector: '$EDGE_CONNECTOR_NAME', - }), - }, - ], -}; -export default config; + { + name: 'Redirect to index.html', + match: '^\\/', + behavior: { + rewrite: `/index.html`, + }, + }, + ], + }, +}); diff --git a/packages/presets/tsconfig.json b/packages/presets/tsconfig.json index 01933ba5..0c85efa5 100644 --- a/packages/presets/tsconfig.json +++ b/packages/presets/tsconfig.json @@ -16,7 +16,6 @@ "azion/utils/edge": ["../utils/src/edge/index.ts"], "azion/utils/node": ["../utils/src/node/index.ts"], "azion/config": ["../config/src/index.ts"], - "azion/config/rules": ["../config/src/rules/index.ts"], "azion/presets": ["../presets/src/index.ts"], "azion/bundler": ["../bundler/src/index.ts"], "azion/types": ["../types/src/index.ts"] From 649695e254df429af023271fe86f31979b50d170 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 18 Jun 2025 19:25:55 +0000 Subject: [PATCH 25/34] chore(release): 1.20.0-stage.12 [skip ci] ## [1.20.0-stage.12](https://github.com/aziontech/lib/compare/v1.20.0-stage.11...v1.20.0-stage.12) (2025-06-18) ### Reverts * api v4 (#176) ([e93e073](https://github.com/aziontech/lib/commit/e93e07306609e3df88ffce7e4a56cf2f414f2640)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e5614c..b6bc9c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.12](https://github.com/aziontech/lib/compare/v1.20.0-stage.11...v1.20.0-stage.12) (2025-06-18) + + +### Reverts + +* api v4 (#176) ([e93e073](https://github.com/aziontech/lib/commit/e93e07306609e3df88ffce7e4a56cf2f414f2640)) + ## [1.20.0-stage.11](https://github.com/aziontech/lib/compare/v1.20.0-stage.10...v1.20.0-stage.11) (2025-06-18) diff --git a/package.json b/package.json index 9b9a27ed..b6772e3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.11", + "version": "1.20.0-stage.12", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 846990f63f4b194e5f9b3e82c687be8f9ede1a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Wed, 18 Jun 2025 21:51:59 -0300 Subject: [PATCH 26/34] fix(presets): astro prebuild --- .../presets/src/presets/astro/prebuild.ts | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/presets/src/presets/astro/prebuild.ts b/packages/presets/src/presets/astro/prebuild.ts index 17633194..16850402 100644 --- a/packages/presets/src/presets/astro/prebuild.ts +++ b/packages/presets/src/presets/astro/prebuild.ts @@ -1,27 +1,46 @@ import { copyDirectory, exec, getPackageManager } from 'azion/utils/node'; -import { readFile } from 'fs/promises'; - -/** - * Runs custom prebuild actions for Astro - */ -async function prebuild() { - const packageManager = await getPackageManager(); - const newOutDir = '.edge/storage'; - let outDir = 'dist'; - - // check if an output path is specified in config file - const configFileContent = await readFile('./astro.config.mjs', 'utf-8'); - const attributeMatch = Array.from(configFileContent.matchAll(/outDir:(.*)\n/g), (match) => match)[0]; - if (attributeMatch) { - outDir = attributeMatch[1].trim(); +import { constants } from 'fs'; +import { access, readFile } from 'fs/promises'; + +const CONFIG_FILES = ['astro.config.ts', 'astro.config.mjs', 'astro.config.js', 'astro.config.cjs'] as const; + +async function getAstroOutDir(): Promise { + for (const configFile of CONFIG_FILES) { + try { + await access(configFile, constants.F_OK); + const configContent = await readFile(configFile, 'utf-8'); + + // Regex supports: outDir: 'path', outDir:"path", outDir: `path` + const outDirMatch = configContent.match(/outDir\s*:\s*['"`]([^'"`]+)['"`]/); + + if (outDirMatch && outDirMatch[1]) { + return outDirMatch[1].trim(); + } + + break; + } catch { + continue; + } } - await exec(`${packageManager} run build`, { - scope: 'Astro', - verbose: true, - }); + return 'dist'; // Default Astro output directory +} + +async function prebuild(): Promise { + try { + const [packageManager, outDir] = await Promise.all([getPackageManager(), getAstroOutDir()]); + + const newOutDir = '.edge/storage'; - copyDirectory(outDir, newOutDir); + await exec(`${packageManager} run build`, { + scope: 'Astro', + verbose: true, + }); + + copyDirectory(outDir, newOutDir); + } catch (error) { + throw new Error(`Erro durante o prebuild do Astro: ${error instanceof Error ? error.message : String(error)}`); + } } export default prebuild; From 8673494eb1cf86de4544a7791db9524866b7ddbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Thu, 19 Jun 2025 18:15:27 -0300 Subject: [PATCH 27/34] fix: rm worker property --- README.md | 3 --- packages/config/README.md | 2 -- packages/config/src/configProcessor/helpers/schema.ts | 4 ---- packages/config/src/types.ts | 1 - 4 files changed, 10 deletions(-) diff --git a/README.md b/README.md index 96a22a48..d5eaf324 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,6 @@ const config = { entry: './src/index.js', preset: 'react', polyfills: true, - worker: true, }, domain: { name: 'example.com', @@ -302,7 +301,6 @@ const config = defineConfig({ }, }, polyfills: true, - worker: true, extend: (config) => { // Customize bundler configuration return { @@ -394,7 +392,6 @@ export default defineConfig({ // Other build configurations entry: './src/index.ts', polyfills: true, - worker: true, memoryFS: { injectionDirs: ['./src/inject'], removePathPrefix: './src', diff --git a/packages/config/README.md b/packages/config/README.md index 49213a8b..a92d5119 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -313,7 +313,6 @@ Type definition for the build configuration. - `preset?: string | AzionBuildPreset` - The preset to be used, can be a string or an AzionBuildPreset object. - `entry?: string | string[] | Record` - The entry file, can be a string, an array of strings, or an object. - `polyfills?: boolean` - Whether to include polyfills. -- `worker?: boolean` - Whether to build a worker. - `extend?: (context: T) => T` - Function to extend the bundler configuration. - `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - In-memory file system configuration. @@ -371,7 +370,6 @@ Type definition for the build configuration. - `setup: BundlerSetup` - Bundler configuration. - \*`bundler?: 'webpack' | 'esbuild'` - The bundler to be used. - `polyfills?: boolean` - Whether to include polyfills. -- `worker?: boolean` - Whether to build a worker. - `extend?: (context: T) => T` - Function to extend the bundler configuration. - `memoryFS?: { injectionDirs: string[], removePathPrefix: string }` - In-memory file system configuration. diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 4880d56c..cdd292e9 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -492,10 +492,6 @@ const azionConfigSchema = { type: 'boolean', errorMessage: "The 'build.polyfills' must be a boolean", }, - worker: { - type: 'boolean', - errorMessage: "The 'build.worker' must be a boolean", - }, extend: { instanceof: 'Function', errorMessage: "The 'build.extend' must be a function", diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9095200c..5bfe6ea7 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -321,7 +321,6 @@ export interface AzionBuild T; memoryFS?: { injectionDirs: string[]; From e2ae89c91cc42a213b8b4303a495fcf3fbbd6b05 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 20 Jun 2025 14:50:19 +0000 Subject: [PATCH 28/34] chore(release): 1.20.0-stage.13 [skip ci] ## [1.20.0-stage.13](https://github.com/aziontech/lib/compare/v1.20.0-stage.12...v1.20.0-stage.13) (2025-06-20) ### Bug Fixes * **preset:** update ruleset of html preset ([90b0def](https://github.com/aziontech/lib/commit/90b0def35c64370cc67c523f5ef95bb2979663b6)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6bc9c68..3e8023b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.13](https://github.com/aziontech/lib/compare/v1.20.0-stage.12...v1.20.0-stage.13) (2025-06-20) + + +### Bug Fixes + +* **preset:** update ruleset of html preset ([90b0def](https://github.com/aziontech/lib/commit/90b0def35c64370cc67c523f5ef95bb2979663b6)) + ## [1.20.0-stage.12](https://github.com/aziontech/lib/compare/v1.20.0-stage.11...v1.20.0-stage.12) (2025-06-18) diff --git a/package.json b/package.json index b6772e3b..bd44d96d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.12", + "version": "1.20.0-stage.13", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From ca726adc772783520e3fa9aa7f39b5beb812c8b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Fri, 20 Jun 2025 14:59:06 -0300 Subject: [PATCH 29/34] feat: add OpenNextjs preset (#179) * feat: add OpenNextjs preset * chore: update request rule name for consistency in OpenNextjs preset --- packages/config/src/types.ts | 1 + packages/presets/src/index.ts | 1 + .../presets/src/presets/opennextjs/config.ts | 58 ++++++++++++++ .../presets/src/presets/opennextjs/index.ts | 6 ++ .../src/presets/opennextjs/metadata.ts | 7 ++ .../src/presets/opennextjs/prebuild.ts | 80 +++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 packages/presets/src/presets/opennextjs/config.ts create mode 100644 packages/presets/src/presets/opennextjs/index.ts create mode 100644 packages/presets/src/presets/opennextjs/metadata.ts create mode 100644 packages/presets/src/presets/opennextjs/prebuild.ts diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9095200c..688c36d7 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -543,6 +543,7 @@ export interface BundlerSetup { export interface BuildContext { production: boolean; handler: BuildEntryPoint; + skipProjectBuild?: boolean; } export type PresetMetadata = { diff --git a/packages/presets/src/index.ts b/packages/presets/src/index.ts index 9debfb3b..186df063 100644 --- a/packages/presets/src/index.ts +++ b/packages/presets/src/index.ts @@ -11,6 +11,7 @@ export * from './presets/javascript'; export * from './presets/jekyll'; export * from './presets/next'; export * from './presets/nuxt'; +export * from './presets/opennextjs'; export * from './presets/preact'; export * from './presets/qwik'; export * from './presets/react'; diff --git a/packages/presets/src/presets/opennextjs/config.ts b/packages/presets/src/presets/opennextjs/config.ts new file mode 100644 index 00000000..b6fdccce --- /dev/null +++ b/packages/presets/src/presets/opennextjs/config.ts @@ -0,0 +1,58 @@ +import type { AzionBuild, AzionConfig } from 'azion/config'; + +const config: AzionConfig = { + build: { + entry: '.open-next/worker.js', + polyfills: true, + bundler: 'esbuild', + preset: 'opennextjs', + } as AzionBuild, + origin: [ + { + name: 'origin-storage-default', + type: 'object_storage', + }, + ], + functions: [ + { + name: 'handler', + path: '.edge/functions/handler.js', + }, + ], + rules: { + request: [ + { + name: 'Set storage origin for all requests _next_static', + match: '^\\/_next\\/static\\/', // starts with '/_next/static/' + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, + }, + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, + }, + { + name: 'Execute Edge Function', + match: '^/', + behavior: { + runFunction: 'handler', + forwardCookies: true, + }, + }, + ], + }, +}; + +export default config; diff --git a/packages/presets/src/presets/opennextjs/index.ts b/packages/presets/src/presets/opennextjs/index.ts new file mode 100644 index 00000000..5495a0b8 --- /dev/null +++ b/packages/presets/src/presets/opennextjs/index.ts @@ -0,0 +1,6 @@ +import type { AzionBuildPreset } from 'azion/config'; +import config from './config'; +import metadata from './metadata'; +import prebuild from './prebuild'; + +export const opennextjs: AzionBuildPreset = { config, metadata, prebuild }; diff --git a/packages/presets/src/presets/opennextjs/metadata.ts b/packages/presets/src/presets/opennextjs/metadata.ts new file mode 100644 index 00000000..c75857ae --- /dev/null +++ b/packages/presets/src/presets/opennextjs/metadata.ts @@ -0,0 +1,7 @@ +import type { PresetMetadata } from 'azion/config'; + +const metadata: PresetMetadata = { + name: 'opennextjs', +}; + +export default metadata; diff --git a/packages/presets/src/presets/opennextjs/prebuild.ts b/packages/presets/src/presets/opennextjs/prebuild.ts new file mode 100644 index 00000000..6b0eb895 --- /dev/null +++ b/packages/presets/src/presets/opennextjs/prebuild.ts @@ -0,0 +1,80 @@ +import { BuildConfiguration, BuildContext } from 'azion/config'; +import { exec } from 'azion/utils/node'; +import { readFile } from 'fs/promises'; +import path from 'path'; + +/** + * Runs custom prebuild actions for OpenNextjs + */ +async function prebuild(buildConfig: BuildConfiguration, ctx: BuildContext): Promise { + const pkgOpenNextjsName = '@aziontech/opennextjs-azion'; + const openNextjsCommand = 'npm exec opennextjs-azion'; + + // Check if the OpenNextjs Azion is installed + const isOpenNextjsInstalled = await checkIfOpenNextjsIsInstalled(); + if (!isOpenNextjsInstalled) { + const packageManager = await indentifyPackageManager(); + const execCommand = + packageManager === 'yarn' + ? `yarn add -D ${pkgOpenNextjsName}` + : packageManager === 'pnpm' + ? `pnpm add -D ${pkgOpenNextjsName}` + : `npm i -D ${pkgOpenNextjsName}`; + await exec(execCommand, { + scope: 'OpenNextjs', + verbose: true, + interactive: true, + }); + } + // Run OpenNextjs command build + if (ctx.production || !ctx.skipProjectBuild) { + const skipBuild = ctx.skipProjectBuild ? '--skipBuild' : ''; + await exec(`${openNextjsCommand} build -- ${skipBuild}`, { + scope: 'OpenNextjs', + verbose: true, + interactive: true, + }); + } + + // Run OpenNextjs commands to populate assets + if (ctx.production) { + await exec(`${openNextjsCommand} populateAssets`, { + scope: 'OpenNextjs', + verbose: true, + }); + } + + // Run OpenNextjs commands to populate cache + if (ctx.production) { + await exec(`${openNextjsCommand} populateCache`, { + scope: 'OpenNextjs', + verbose: true, + }); + } +} + +async function checkIfOpenNextjsIsInstalled(): Promise { + const packageJsonPath = path.resolve(process.cwd(), 'package.json'); + const packageJson = await readFile(packageJsonPath, 'utf-8'); + const packageJsonObj = JSON.parse(packageJson); + if (!packageJsonObj.devDependencies || !packageJsonObj.devDependencies['@aziontech/opennextjs-azion']) { + return false; + } + return true; +} + +async function indentifyPackageManager(): Promise { + const lockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']; + for (const lockFile of lockFiles) { + const filePath = path.resolve(process.cwd(), lockFile); + try { + await readFile(filePath, 'utf-8'); + return lockFile.includes('yarn') ? 'yarn' : lockFile.includes('pnpm') ? 'pnpm' : 'npm'; + } catch (error) { + // File not found, continue to the next one + } + } + return 'npm'; // Default to npm if no lock file is found +} + +export default prebuild; From cb11a8e8a153b17d80898f7ff2a6000c7f8eebc2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 20 Jun 2025 18:00:23 +0000 Subject: [PATCH 30/34] chore(release): 1.20.0-stage.14 [skip ci] ## [1.20.0-stage.14](https://github.com/aziontech/lib/compare/v1.20.0-stage.13...v1.20.0-stage.14) (2025-06-20) ### Features * add OpenNextjs preset (#179) ([ca726ad](https://github.com/aziontech/lib/commit/ca726adc772783520e3fa9aa7f39b5beb812c8b1)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e8023b1..d928241c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.14](https://github.com/aziontech/lib/compare/v1.20.0-stage.13...v1.20.0-stage.14) (2025-06-20) + + +### Features + +* add OpenNextjs preset (#179) ([ca726ad](https://github.com/aziontech/lib/commit/ca726adc772783520e3fa9aa7f39b5beb812c8b1)) + ## [1.20.0-stage.13](https://github.com/aziontech/lib/compare/v1.20.0-stage.12...v1.20.0-stage.13) (2025-06-20) diff --git a/package.json b/package.json index bd44d96d..87a6702f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.13", + "version": "1.20.0-stage.14", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From ada2ea5ea5843bba934084c92b101d8b842dd99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Fri, 20 Jun 2025 15:11:11 -0300 Subject: [PATCH 31/34] chore: fix message lang --- packages/presets/src/presets/astro/prebuild.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/presets/src/presets/astro/prebuild.ts b/packages/presets/src/presets/astro/prebuild.ts index 16850402..798f11eb 100644 --- a/packages/presets/src/presets/astro/prebuild.ts +++ b/packages/presets/src/presets/astro/prebuild.ts @@ -39,7 +39,7 @@ async function prebuild(): Promise { copyDirectory(outDir, newOutDir); } catch (error) { - throw new Error(`Erro durante o prebuild do Astro: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Error during Astro prebuild: ${error instanceof Error ? error.message : String(error)}`); } } From 3b28111b5704a017ac27cf9aa88c9a4d588e4351 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 20 Jun 2025 18:32:56 +0000 Subject: [PATCH 32/34] chore(release): 1.20.0-stage.15 [skip ci] ## [1.20.0-stage.15](https://github.com/aziontech/lib/compare/v1.20.0-stage.14...v1.20.0-stage.15) (2025-06-20) ### Bug Fixes * **presets:** astro prebuild ([846990f](https://github.com/aziontech/lib/commit/846990f63f4b194e5f9b3e82c687be8f9ede1a65)) * rm worker property ([8673494](https://github.com/aziontech/lib/commit/8673494eb1cf86de4544a7791db9524866b7ddbb)) --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d928241c..a8f71ed7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [1.20.0-stage.15](https://github.com/aziontech/lib/compare/v1.20.0-stage.14...v1.20.0-stage.15) (2025-06-20) + + +### Bug Fixes + +* **presets:** astro prebuild ([846990f](https://github.com/aziontech/lib/commit/846990f63f4b194e5f9b3e82c687be8f9ede1a65)) +* rm worker property ([8673494](https://github.com/aziontech/lib/commit/8673494eb1cf86de4544a7791db9524866b7ddbb)) + ## [1.20.0-stage.14](https://github.com/aziontech/lib/compare/v1.20.0-stage.13...v1.20.0-stage.14) (2025-06-20) diff --git a/package.json b/package.json index 87a6702f..359f12b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.14", + "version": "1.20.0-stage.15", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky", From 5252f0acfb43a296bcdd291524a5b62de01df75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Filho?= Date: Fri, 20 Jun 2025 15:39:24 -0300 Subject: [PATCH 33/34] fix: rename skipProjectBuild to skipFrameworkBuild in BuildContext (#180) --- packages/config/src/types.ts | 2 +- packages/presets/src/presets/opennextjs/prebuild.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 01810a72..6541e039 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -542,7 +542,7 @@ export interface BundlerSetup { export interface BuildContext { production: boolean; handler: BuildEntryPoint; - skipProjectBuild?: boolean; + skipFrameworkBuild?: boolean; } export type PresetMetadata = { diff --git a/packages/presets/src/presets/opennextjs/prebuild.ts b/packages/presets/src/presets/opennextjs/prebuild.ts index 6b0eb895..c686e2a9 100644 --- a/packages/presets/src/presets/opennextjs/prebuild.ts +++ b/packages/presets/src/presets/opennextjs/prebuild.ts @@ -27,8 +27,8 @@ async function prebuild(buildConfig: BuildConfiguration, ctx: BuildContext): Pro }); } // Run OpenNextjs command build - if (ctx.production || !ctx.skipProjectBuild) { - const skipBuild = ctx.skipProjectBuild ? '--skipBuild' : ''; + if (ctx.production || !ctx.skipFrameworkBuild) { + const skipBuild = ctx.skipFrameworkBuild ? '--skipBuild' : ''; await exec(`${openNextjsCommand} build -- ${skipBuild}`, { scope: 'OpenNextjs', verbose: true, From 404cadbc54a97a5e0b30af4a56387d815abfccb2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 20 Jun 2025 18:40:38 +0000 Subject: [PATCH 34/34] chore(release): 1.20.0-stage.16 [skip ci] ## [1.20.0-stage.16](https://github.com/aziontech/lib/compare/v1.20.0-stage.15...v1.20.0-stage.16) (2025-06-20) ### Bug Fixes * rename skipProjectBuild to skipFrameworkBuild in BuildContext (#180) ([5252f0a](https://github.com/aziontech/lib/commit/5252f0acfb43a296bcdd291524a5b62de01df75d)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f71ed7..e1bc9e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.0-stage.16](https://github.com/aziontech/lib/compare/v1.20.0-stage.15...v1.20.0-stage.16) (2025-06-20) + + +### Bug Fixes + +* rename skipProjectBuild to skipFrameworkBuild in BuildContext (#180) ([5252f0a](https://github.com/aziontech/lib/commit/5252f0acfb43a296bcdd291524a5b62de01df75d)) + ## [1.20.0-stage.15](https://github.com/aziontech/lib/compare/v1.20.0-stage.14...v1.20.0-stage.15) (2025-06-20) diff --git a/package.json b/package.json index 359f12b4..c7a80965 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azion", - "version": "1.20.0-stage.15", + "version": "1.20.0-stage.16", "description": "Azion Packages for Edge Computing.", "scripts": { "prepare": "husky",