diff --git a/package.json b/package.json index 3029dc09..39c3b0fd 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "md5": "^2.2.1" }, "devDependencies": { + "@apollo/server": "4.9.3", "@changesets/cli": "2.26.2", "@commitlint/cli": "17.7.1", "@commitlint/config-angular": "17.7.0", @@ -71,7 +72,6 @@ "@types/supertest": "2.0.12", "@typescript-eslint/eslint-plugin": "6.6.0", "@typescript-eslint/parser": "6.6.0", - "@apollo/server": "4.9.3", "apollo-server-fastify": "3.12.1", "conventional-changelog-cli": "4.0.0", "cz-conventional-changelog": "3.3.0", @@ -84,6 +84,7 @@ "jest": "29.6.4", "lint-staged": "14.0.1", "nodemon": "3.0.1", + "pactum": "^3.4.1", "pinst": "3.0.0", "prettier": "3.0.3", "reflect-metadata": "0.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e35531c5..2c2a6174 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ devDependencies: nodemon: specifier: 3.0.1 version: 3.0.1 + pactum: + specifier: ^3.4.1 + version: 3.4.1 pinst: specifier: 3.0.0 version: 3.0.0 @@ -144,7 +147,7 @@ devDependencies: version: 29.1.1(@babel/core@7.22.5)(jest@29.6.4)(typescript@5.2.2) ts-loader: specifier: 9.4.4 - version: 9.4.4(typescript@5.2.2)(webpack@5.88.2) + version: 9.4.4(typescript@5.2.2)(webpack@5.88.1) ts-node: specifier: 10.9.1 version: 10.9.1(@types/node@18.17.14)(typescript@5.2.2) @@ -287,7 +290,7 @@ packages: response-iterator: 0.2.6 symbol-observable: 4.0.0 ts-invariant: 0.10.3 - tslib: 2.6.2 + tslib: 2.6.0 zen-observable-ts: 1.2.5 dev: true optional: true @@ -571,6 +574,11 @@ packages: xss: 1.0.14 dev: true + /@arr/every@1.0.1: + resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} + engines: {node: '>=4'} + dev: true + /@babel/code-frame@7.22.5: resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} engines: {node: '>=6.9.0'} @@ -601,7 +609,7 @@ packages: debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 - semver: 6.3.1 + semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true @@ -627,7 +635,7 @@ packages: '@babel/helper-validator-option': 7.22.5 browserslist: 4.21.9 lru-cache: 5.1.1 - semver: 6.3.1 + semver: 6.3.0 dev: true /@babel/helper-environment-visitor@7.22.5: @@ -721,7 +729,6 @@ packages: /@babel/highlight@7.22.5: resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} engines: {node: '>=6.9.0'} - requiresBuild: true dependencies: '@babel/helper-validator-identifier': 7.22.5 chalk: 2.4.2 @@ -927,7 +934,7 @@ packages: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.5.4 + semver: 7.5.3 dev: true /@changesets/assemble-release-plan@5.2.4: @@ -938,7 +945,7 @@ packages: '@changesets/get-dependents-graph': 1.3.6 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 - semver: 7.5.4 + semver: 7.5.3 dev: true /@changesets/changelog-git@0.1.14: @@ -1011,7 +1018,7 @@ packages: '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 - semver: 7.5.4 + semver: 7.5.3 dev: true /@changesets/get-release-plan@3.0.17: @@ -1135,10 +1142,19 @@ packages: '@commitlint/config-angular-type-enum': 17.4.0 dev: true + /@commitlint/config-validator@17.4.4: + resolution: {integrity: sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==} + engines: {node: '>=v14'} + requiresBuild: true + dependencies: + '@commitlint/types': 17.4.4 + ajv: 8.12.0 + dev: true + optional: true + /@commitlint/config-validator@17.6.7: resolution: {integrity: sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==} engines: {node: '>=v14'} - requiresBuild: true dependencies: '@commitlint/types': 17.4.4 ajv: 8.12.0 @@ -1159,7 +1175,6 @@ packages: /@commitlint/execute-rule@17.4.0: resolution: {integrity: sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==} engines: {node: '>=v14'} - requiresBuild: true dev: true /@commitlint/format@17.4.4: @@ -1188,10 +1203,34 @@ packages: '@commitlint/types': 17.4.4 dev: true + /@commitlint/load@17.5.0: + resolution: {integrity: sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q==} + engines: {node: '>=v14'} + requiresBuild: true + dependencies: + '@commitlint/config-validator': 17.4.4 + '@commitlint/execute-rule': 17.4.0 + '@commitlint/resolve-extends': 17.4.4 + '@commitlint/types': 17.4.4 + '@types/node': 18.17.14 + chalk: 4.1.2 + cosmiconfig: 8.2.0 + cosmiconfig-typescript-loader: 4.3.0(@types/node@18.17.14)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.2.2) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + resolve-from: 5.0.0 + ts-node: 10.9.1(@types/node@18.17.14)(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + dev: true + optional: true + /@commitlint/load@17.7.1: resolution: {integrity: sha512-S/QSOjE1ztdogYj61p6n3UbkUvweR17FQ0zDbNtoTLc+Hz7vvfS7ehoTMQ27hPSjVBpp7SzEcOQu081RLjKHJQ==} engines: {node: '>=v14'} - requiresBuild: true dependencies: '@commitlint/config-validator': 17.6.7 '@commitlint/execute-rule': 17.4.0 @@ -1237,10 +1276,23 @@ packages: minimist: 1.2.8 dev: true + /@commitlint/resolve-extends@17.4.4: + resolution: {integrity: sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==} + engines: {node: '>=v14'} + requiresBuild: true + dependencies: + '@commitlint/config-validator': 17.4.4 + '@commitlint/types': 17.4.4 + import-fresh: 3.3.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + dev: true + optional: true + /@commitlint/resolve-extends@17.6.7: resolution: {integrity: sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==} engines: {node: '>=v14'} - requiresBuild: true dependencies: '@commitlint/config-validator': 17.6.7 '@commitlint/types': 17.4.4 @@ -1297,8 +1349,13 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/regexpp@4.6.2: - resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==} + /@eslint-community/regexpp@4.5.1: + resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint-community/regexpp@4.8.0: + resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true @@ -1324,6 +1381,10 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@exodus/schemasafe@1.0.1: + resolution: {integrity: sha512-PQdbF8dGd4LnbwBlcc4ML8RKYdplm+e9sUeWBTr4zgF13/Shiuov9XznvM4T8cb1CfyKK21yTUkuAIIh/DAH/g==} + dev: true + /@fastify/accepts@3.0.0: resolution: {integrity: sha512-+ldBB3O59p/z9Uc1LSZqAA4/oZaNbRtCVMwjgJOahl+PKmx4ciRRoWVht77kFOb36lRE5MPEba4Vt78H7PKfQw==} engines: {node: '>=10'} @@ -1401,7 +1462,7 @@ packages: dependencies: '@graphql-tools/utils': 8.9.0(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/merge@8.4.2(graphql@16.8.0): @@ -1411,7 +1472,7 @@ packages: dependencies: '@graphql-tools/utils': 9.2.1(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/merge@9.0.0(graphql@16.8.0): @@ -1422,7 +1483,7 @@ packages: dependencies: '@graphql-tools/utils': 10.0.1(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/mock@8.7.20(graphql@16.8.0): @@ -1434,7 +1495,7 @@ packages: '@graphql-tools/utils': 9.2.1(graphql@16.8.0) fast-json-stable-stringify: 2.1.0 graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/schema@10.0.0(graphql@16.8.0): @@ -1446,7 +1507,7 @@ packages: '@graphql-tools/merge': 9.0.0(graphql@16.8.0) '@graphql-tools/utils': 10.0.1(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 value-or-promise: 1.0.12 dev: true @@ -1458,7 +1519,7 @@ packages: '@graphql-tools/merge': 8.3.1(graphql@16.8.0) '@graphql-tools/utils': 8.9.0(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 value-or-promise: 1.0.11 dev: true @@ -1470,7 +1531,7 @@ packages: '@graphql-tools/merge': 8.4.2(graphql@16.8.0) '@graphql-tools/utils': 9.2.1(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 value-or-promise: 1.0.12 dev: true @@ -1482,7 +1543,7 @@ packages: dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/utils@8.9.0(graphql@16.8.0): @@ -1491,7 +1552,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-tools/utils@9.2.1(graphql@16.8.0): @@ -1501,7 +1562,7 @@ packages: dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.0) graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /@graphql-typed-document-node/core@3.2.0(graphql@16.8.0): @@ -1630,11 +1691,11 @@ packages: jest-mock: 29.6.3 dev: true - /@jest/expect-utils@29.6.3: - resolution: {integrity: sha512-nvOEW4YoqRKD9HBJ9OJ6przvIvP9qilp5nAn1462P5ZlL/MM9SgPEZFyjTGPfs7QkocdUsJa6KjHhyRn4ueItA==} + /@jest/expect-utils@29.6.1: + resolution: {integrity: sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.6.3 + jest-get-type: 29.4.3 dev: true /@jest/expect-utils@29.6.4: @@ -1715,6 +1776,13 @@ packages: - supports-color dev: true + /@jest/schemas@29.6.0: + resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1774,6 +1842,30 @@ packages: - supports-color dev: true + /@jest/types@29.6.0: + resolution: {integrity: sha512-8XCgL9JhqbJTFnMRjEAO+TuW251+MoMd5BSzLiE3vvzpQ8RlBxy8NoyNkDhs3K3OL3HeVinlOl9or5p7GTeOLg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.17.14 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + + /@jest/types@29.6.1: + resolution: {integrity: sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.0 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.17.14 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + /@jest/types@29.6.3: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2353,6 +2445,10 @@ packages: config-chain: 1.1.13 dev: true + /@polka/url@0.5.0: + resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} + dev: true + /@protobufjs/aspromise@1.1.2: resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} dev: true @@ -2479,17 +2575,17 @@ packages: dependencies: '@semantic-release/error': 4.0.0 aggregate-error: 4.0.1 - execa: 7.2.0 + execa: 7.1.1 fs-extra: 11.1.1 lodash-es: 4.17.21 nerf-dart: 1.0.0 normalize-url: 8.0.0 npm: 9.7.1 rc: 1.2.8 - read-pkg: 8.1.0 + read-pkg: 8.0.0 registry-auth-token: 5.0.2 semantic-release: 21.0.5 - semver: 7.5.4 + semver: 7.5.3 tempy: 3.0.0 dev: true @@ -2673,8 +2769,8 @@ packages: /@types/jest@29.5.4: resolution: {integrity: sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==} dependencies: - expect: 29.6.3 - pretty-format: 29.6.3 + expect: 29.6.1 + pretty-format: 29.6.1 dev: true /@types/json-schema@7.0.12: @@ -2726,7 +2822,6 @@ packages: /@types/node@20.4.7: resolution: {integrity: sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==} - requiresBuild: true dev: true /@types/normalize-package-data@2.4.1: @@ -2801,19 +2896,28 @@ packages: typescript: optional: true dependencies: +<<<<<<< HEAD '@eslint-community/regexpp': 4.6.2 '@typescript-eslint/parser': 6.6.0(eslint@8.48.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 6.6.0 '@typescript-eslint/type-utils': 6.6.0(eslint@8.48.0)(typescript@5.2.2) '@typescript-eslint/utils': 6.6.0(eslint@8.48.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.6.0 +======= + '@eslint-community/regexpp': 4.5.1 + '@typescript-eslint/parser': 6.5.0(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.5.0 + '@typescript-eslint/type-utils': 6.5.0(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.5.0(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.5.0 +>>>>>>> 2767bdd (feat: allowfor multiple throttler contexts) debug: 4.3.4 eslint: 8.48.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.2.2) + ts-api-utils: 1.0.2(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -2862,7 +2966,7 @@ packages: '@typescript-eslint/utils': 6.6.0(eslint@8.48.0)(typescript@5.2.2) debug: 4.3.4 eslint: 8.48.0 - ts-api-utils: 1.0.1(typescript@5.2.2) + ts-api-utils: 1.0.2(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -2888,7 +2992,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.2.2) + ts-api-utils: 1.0.2(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -2917,8 +3021,13 @@ packages: resolution: {integrity: sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: +<<<<<<< HEAD '@typescript-eslint/types': 6.6.0 eslint-visitor-keys: 3.4.3 +======= + '@typescript-eslint/types': 6.5.0 + eslint-visitor-keys: 3.4.1 +>>>>>>> 2767bdd (feat: allowfor multiple throttler contexts) dev: true /@webassemblyjs/ast@1.11.6: @@ -3032,7 +3141,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.6.0 dev: true optional: true @@ -3041,7 +3150,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.6.0 dev: true optional: true @@ -3050,7 +3159,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.6.0 dev: true optional: true @@ -3059,7 +3168,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.6.0 dev: true optional: true @@ -3413,7 +3522,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - requiresBuild: true dev: true /argv-formatter@1.0.0: @@ -3455,13 +3563,13 @@ packages: engines: {node: '>=8'} dev: true - /array.prototype.findlastindex@1.2.2: - resolution: {integrity: sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==} + /array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 dev: true @@ -3486,6 +3594,18 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -3826,6 +3946,10 @@ packages: redeyed: 2.1.1 dev: true + /centra@2.6.0: + resolution: {integrity: sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ==} + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3843,6 +3967,11 @@ packages: supports-color: 7.2.0 dev: true + /chalk@5.2.0: + resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /chalk@5.3.0: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -4129,7 +4258,7 @@ packages: dependencies: add-stream: 1.0.0 conventional-changelog: 5.0.0 - meow: 12.1.0 + meow: 12.1.1 tempfile: 5.0.0 dev: true @@ -4138,8 +4267,8 @@ packages: engines: {node: '>=16'} dev: true - /conventional-changelog-conventionalcommits@7.0.0: - resolution: {integrity: sha512-iu2Zt5f7ESwpGJStnbHAG2fL/kBA5h/s1AuDYGKqb7gQcXpWlyfGiF8wM+mfFD5M/cYt9Hl3iWNDm3n72qBA1w==} + /conventional-changelog-conventionalcommits@7.0.1: + resolution: {integrity: sha512-VfFJxBmi+LYXeb4pIfZGbuaFCpWZh0qXbUAKP/s6B8tigV6R4D8j5PDlTtMMWawa7+DcNySVoF7kPWz0EMYuCQ==} engines: {node: '>=16'} dependencies: compare-func: 2.0.0 @@ -4204,7 +4333,7 @@ packages: handlebars: 4.7.7 json-stringify-safe: 5.0.1 meow: 8.1.2 - semver: 6.3.1 + semver: 6.3.0 split: 1.0.1 dev: true @@ -4216,8 +4345,8 @@ packages: conventional-commits-filter: 4.0.0 handlebars: 4.7.7 json-stringify-safe: 5.0.1 - meow: 12.1.0 - semver: 7.5.4 + meow: 12.1.1 + semver: 7.5.3 split2: 4.2.0 dev: true @@ -4228,7 +4357,7 @@ packages: conventional-changelog-angular: 7.0.0 conventional-changelog-atom: 4.0.0 conventional-changelog-codemirror: 4.0.0 - conventional-changelog-conventionalcommits: 7.0.0 + conventional-changelog-conventionalcommits: 7.0.1 conventional-changelog-core: 6.0.0 conventional-changelog-ember: 4.0.0 conventional-changelog-eslint: 5.0.0 @@ -4273,7 +4402,7 @@ packages: dependencies: JSONStream: 1.3.5 is-text-path: 2.0.0 - meow: 12.1.0 + meow: 12.1.1 split2: 4.2.0 dev: true @@ -4315,10 +4444,25 @@ packages: vary: 1.1.2 dev: true + /cosmiconfig-typescript-loader@4.3.0(@types/node@18.17.14)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.2.2): + resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=7' + ts-node: '>=10' + typescript: '>=3' + dependencies: + '@types/node': 18.17.14 + cosmiconfig: 8.2.0 + ts-node: 10.9.1(@types/node@18.17.14)(typescript@5.2.2) + typescript: 5.2.2 + dev: true + optional: true + /cosmiconfig-typescript-loader@4.3.0(@types/node@20.4.7)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.2.2): resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} - requiresBuild: true peerDependencies: '@types/node': '*' cosmiconfig: '>=7' @@ -4421,7 +4565,7 @@ packages: longest: 2.0.1 word-wrap: 1.2.3 optionalDependencies: - '@commitlint/load': 17.7.1 + '@commitlint/load': 17.5.0 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -4493,8 +4637,8 @@ packages: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true - /dedent@1.2.0: - resolution: {integrity: sha512-i4tcg0ClgvMUSxwHpt+NHQ01ZJmAkl6eBvDNrSZG9e+oLRTCSHv0wpr/Bzjpf6CwKeIHGevE1M34Y1Axdms5VQ==} + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -4511,6 +4655,10 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /deep-override@1.0.2: + resolution: {integrity: sha512-+bAuLuYqaVVUWPaq8rmU8NLTX85p4I5k5/cVdhBioEfH7k+5NlGdv4NoJVQcJRByqzzTWWzTpih+pU1wBTmMow==} + dev: true + /deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -4571,6 +4719,11 @@ packages: wrappy: 1.0.2 dev: true + /diff-sequences@29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4695,7 +4848,7 @@ packages: resolution: {integrity: sha512-ZCEas2sDVFR3gpumwwzSU4OJZwWJ46yqJH3TqH3vSxEBzeAlC0uCJLGAnZC0vX1TIXzHzjcwpKmUn2xw5mC/qA==} engines: {node: ^16.14 || >=18} dependencies: - execa: 7.2.0 + execa: 7.1.1 java-properties: 1.0.2 dev: true @@ -4745,6 +4898,51 @@ packages: which-typed-array: 1.1.9 dev: true + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.11 + dev: true + /es-module-lexer@1.3.0: resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} dev: true @@ -4816,7 +5014,7 @@ packages: dependencies: debug: 3.2.7(supports-color@5.5.0) is-core-module: 2.13.0 - resolve: 1.22.3 + resolve: 1.22.2 transitivePeerDependencies: - supports-color dev: true @@ -4862,7 +5060,7 @@ packages: dependencies: '@typescript-eslint/parser': 6.6.0(eslint@8.48.0)(typescript@5.2.2) array-includes: 3.1.6 - array.prototype.findlastindex: 1.2.2 + array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7(supports-color@5.5.0) @@ -4874,8 +5072,8 @@ packages: is-core-module: 2.13.0 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.6 - object.groupby: 1.0.0 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 object.values: 1.1.6 semver: 6.3.1 tsconfig-paths: 3.14.2 @@ -4901,6 +5099,11 @@ packages: estraverse: 5.3.0 dev: true + /eslint-visitor-keys@3.4.1: + resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4912,7 +5115,7 @@ packages: hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) - '@eslint-community/regexpp': 4.6.2 + '@eslint-community/regexpp': 4.8.0 '@eslint/eslintrc': 2.1.2 '@eslint/js': 8.48.0 '@humanwhocodes/config-array': 0.11.10 @@ -5049,6 +5252,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa@7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} @@ -5076,15 +5294,16 @@ packages: homedir-polyfill: 1.0.3 dev: true - /expect@29.6.3: - resolution: {integrity: sha512-x1vY4LlEMWUYVZQrFi4ZANXFwqYbJ/JNQspLVvzhW2BNY28aNcXMQH6imBbt+RBf5sVRTodYHXtSP/TLEU0Dxw==} + /expect@29.6.1: + resolution: {integrity: sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/expect-utils': 29.6.3 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.6.3 - jest-message-util: 29.6.3 - jest-util: 29.6.3 + '@jest/expect-utils': 29.6.1 + '@types/node': 18.17.14 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.6.1 + jest-message-util: 29.6.1 + jest-util: 29.6.1 dev: true /expect@29.6.4: @@ -5246,7 +5465,7 @@ packages: proxy-addr: 2.0.7 rfdc: 1.3.0 secure-json-parse: 2.7.0 - semver: 7.5.4 + semver: 7.5.3 tiny-lru: 8.0.2 transitivePeerDependencies: - supports-color @@ -5269,7 +5488,7 @@ packages: proxy-addr: 2.0.7 rfdc: 1.3.0 secure-json-parse: 2.7.0 - semver: 7.5.4 + semver: 7.5.3 tiny-lru: 11.0.1 transitivePeerDependencies: - supports-color @@ -5470,12 +5689,20 @@ packages: minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.5.4 + semver: 7.5.3 tapable: 2.2.1 typescript: 5.1.6 webpack: 5.88.2 dev: true + /form-data-lite@1.0.3: + resolution: {integrity: sha512-P7xPqAiOPKzC9Q9aywAZJCQq4QOE5WokPb3HrcWRh7C57RKytueJzoORZAVgHBNvK/lL7E+FxjQjd4X/zbecEQ==} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-lite: 1.0.3 + dev: true + /form-data@3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} @@ -5592,7 +5819,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 functions-have-names: 1.2.3 dev: true @@ -5678,7 +5905,7 @@ packages: hasBin: true dependencies: dargs: 8.1.0 - meow: 12.1.0 + meow: 12.1.1 split2: 4.2.0 dev: true @@ -5695,8 +5922,8 @@ packages: engines: {node: '>=16'} hasBin: true dependencies: - meow: 12.1.0 - semver: 7.5.4 + meow: 12.1.1 + semver: 7.5.3 dev: true /gitconfiglocal@1.0.0: @@ -5854,7 +6081,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.8.0 - tslib: 2.6.2 + tslib: 2.6.0 dev: true /graphql-tools@9.0.0(graphql@16.8.0): @@ -6238,7 +6465,6 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - requiresBuild: true dev: true /is-bigint@1.0.4: @@ -6278,6 +6504,12 @@ packages: ci-info: 3.8.0 dev: true + /is-core-module@2.12.1: + resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} + dependencies: + has: 1.0.3 + dev: true + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: @@ -6459,6 +6691,10 @@ packages: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: true + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -6487,7 +6723,7 @@ packages: '@babel/parser': 7.22.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 - semver: 6.3.1 + semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true @@ -6576,7 +6812,7 @@ packages: '@types/node': 18.17.14 chalk: 4.1.2 co: 4.6.0 - dedent: 1.2.0 + dedent: 1.5.1 is-generator-fn: 2.1.0 jest-each: 29.6.3 jest-matcher-utils: 29.6.4 @@ -6664,14 +6900,14 @@ packages: - supports-color dev: true - /jest-diff@29.6.3: - resolution: {integrity: sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==} + /jest-diff@29.6.1: + resolution: {integrity: sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.6.3 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.6.1 dev: true /jest-diff@29.6.4: @@ -6714,6 +6950,11 @@ packages: jest-util: 29.6.3 dev: true + /jest-get-type@29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6746,14 +6987,14 @@ packages: pretty-format: 29.6.3 dev: true - /jest-matcher-utils@29.6.3: - resolution: {integrity: sha512-6ZrMYINZdwduSt5Xu18/n49O1IgXdjsfG7NEZaQws9k69eTKWKcVbJBw/MZsjOZe2sSyJFmuzh8042XWwl54Zg==} + /jest-matcher-utils@29.6.1: + resolution: {integrity: sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - jest-diff: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.6.3 + jest-diff: 29.6.1 + jest-get-type: 29.4.3 + pretty-format: 29.6.1 dev: true /jest-matcher-utils@29.6.4: @@ -6766,6 +7007,21 @@ packages: pretty-format: 29.6.3 dev: true + /jest-message-util@29.6.1: + resolution: {integrity: sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.5 + '@jest/types': 29.6.1 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.6.1 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + /jest-message-util@29.6.3: resolution: {integrity: sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6827,7 +7083,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@29.6.4) jest-util: 29.6.3 jest-validate: 29.6.3 - resolve: 1.22.3 + resolve: 1.22.2 resolve.exports: 2.0.2 slash: 3.0.0 dev: true @@ -6914,7 +7170,7 @@ packages: jest-util: 29.6.3 natural-compare: 1.4.0 pretty-format: 29.6.3 - semver: 7.5.4 + semver: 7.5.3 transitivePeerDependencies: - supports-color dev: true @@ -6923,7 +7179,19 @@ packages: resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.6.3 + '@jest/types': 29.6.0 + '@types/node': 18.17.14 + chalk: 4.1.2 + ci-info: 3.8.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-util@29.6.1: + resolution: {integrity: sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.1 '@types/node': 18.17.14 chalk: 4.1.2 ci-info: 3.8.0 @@ -7011,7 +7279,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - requiresBuild: true dev: true /js-yaml@3.14.1: @@ -7048,13 +7315,16 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true + /json-query@2.2.2: + resolution: {integrity: sha512-y+IcVZSdqNmS4fO8t1uZF6RMMs0xh3SrTjJr9bp1X3+v0Q13+7Cyv12dSmKwDswp/H427BVtpkLWhGxYu3ZWRA==} + dev: true + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - requiresBuild: true dev: true /json-stable-stringify-without-jsonify@1.0.1: @@ -7116,6 +7386,11 @@ packages: engines: {node: '>=6'} dev: true + /klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + dev: true + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -7146,6 +7421,10 @@ packages: set-cookie-parser: 2.6.0 dev: true + /lightcookie@1.0.25: + resolution: {integrity: sha512-SrY/+eBPaKAMnsn7mCsoOMZzoQyCyHHHZlFCu2fjo28XxSyCLjlooKiTxyrXTg8NPaHp1YzWi0lcGG1gDi6KHw==} + dev: true + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -7153,7 +7432,6 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - requiresBuild: true dev: true /lines-and-columns@2.0.3: @@ -7302,7 +7580,6 @@ packages: /lodash.mergewith@4.6.2: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - requiresBuild: true dev: true /lodash.snakecase@4.1.1: @@ -7319,7 +7596,6 @@ packages: /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - requiresBuild: true dev: true /lodash.uniqby@4.7.0: @@ -7439,7 +7715,7 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: - semver: 6.3.1 + semver: 6.3.0 dev: true /make-error@1.3.6: @@ -7470,7 +7746,7 @@ packages: dependencies: ansi-escapes: 6.2.0 cardinal: 2.1.1 - chalk: 5.3.0 + chalk: 5.2.0 cli-table3: 0.6.3 marked: 5.1.0 node-emoji: 1.11.0 @@ -7483,6 +7759,13 @@ packages: hasBin: true dev: true + /matchit@1.1.0: + resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==} + engines: {node: '>=6'} + dependencies: + '@arr/every': 1.0.1 + dev: true + /md5@2.2.1: resolution: {integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==} dependencies: @@ -7503,8 +7786,8 @@ packages: fs-monkey: 1.0.4 dev: true - /meow@12.1.0: - resolution: {integrity: sha512-SvSqzS5ktjGoySdCwxQI16iO/ID1LtxM03QvJ4FF2H5cCtXLN7YbfKBCL9btqXSSuJ5TNG4UH6wvWtXZuvgvrw==} + /meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} dev: true @@ -7577,6 +7860,10 @@ packages: engines: {node: '>= 0.6'} dev: true + /mime-lite@1.0.3: + resolution: {integrity: sha512-V85l97zJSTG8FEvmdTlmNYb0UMrVBwvRjw7bWTf/aT6KjFwtz3iTz8D2tuFIp7lwiaO2C5ecnrEmSkkMRCrqVw==} + dev: true + /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -7791,7 +8078,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.3 + resolve: 1.22.2 semver: 5.7.1 validate-npm-package-license: 3.0.4 dev: true @@ -7801,8 +8088,8 @@ packages: engines: {node: '>=10'} dependencies: hosted-git-info: 4.1.0 - is-core-module: 2.13.0 - semver: 7.5.4 + is-core-module: 2.12.1 + semver: 7.5.3 validate-npm-package-license: 3.0.4 dev: true @@ -7811,8 +8098,8 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.13.0 - semver: 7.5.4 + is-core-module: 2.12.1 + semver: 7.5.3 validate-npm-package-license: 3.0.4 dev: true @@ -7821,8 +8108,8 @@ packages: engines: {node: ^16.14.0 || >=18.0.0} dependencies: hosted-git-info: 7.0.0 - is-core-module: 2.13.0 - semver: 7.5.4 + is-core-module: 2.12.1 + semver: 7.5.3 validate-npm-package-license: 3.0.4 dev: true @@ -7951,21 +8238,21 @@ packages: object-keys: 1.1.1 dev: true - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 dev: true - /object.groupby@1.0.0: - resolution: {integrity: sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==} + /object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 get-intrinsic: 1.2.1 dev: true @@ -8013,6 +8300,12 @@ packages: mimic-fn: 4.0.0 dev: true + /openapi-fuzzer-core@1.0.6: + resolution: {integrity: sha512-FJNJIfgUFuv4NmVGq9MYdoKra2GrkDy2uhIjE2YGlw30UA1glf4SXLMhI4UwdcJ8jisKdIxi7lXrfej8GvNW5w==} + dependencies: + klona: 2.0.6 + dev: true + /optimism@0.16.2: resolution: {integrity: sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ==} requiresBuild: true @@ -8178,14 +8471,38 @@ packages: engines: {node: '>=6'} dev: true + /pactum-matchers@1.1.5: + resolution: {integrity: sha512-Bc9VVmCYeTFLRw+Du/i8MJl8jA0iBP1pbhYg45+shrKkZJbhSTsrL8ZyAKLuGeFnO0e1HG1c7u6O9Cua30a9cg==} + dev: true + + /pactum@3.4.1: + resolution: {integrity: sha512-rRo2qtrUCdzjjKC+o5GeET+OsIMxxejimPh0tz7w9MB6ZDQgI0WJaXjVbZ0+/CtWPrKD7cyWIWdIJMNVJxsQxg==} + engines: {node: '>=10'} + dependencies: + '@exodus/schemasafe': 1.0.1 + deep-override: 1.0.2 + form-data-lite: 1.0.3 + json-query: 2.2.2 + klona: 2.0.6 + lightcookie: 1.0.25 + openapi-fuzzer-core: 1.0.6 + pactum-matchers: 1.1.5 + parse-graphql: 1.0.0 + phin: 3.7.0 + polka: 0.5.2 + dev: true + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - requiresBuild: true dependencies: callsites: 3.1.0 dev: true + /parse-graphql@1.0.0: + resolution: {integrity: sha512-NjvQHHaiPCxPZrhm/kKnorxOv7r/eA+tE0VW5E8iJMH9wTqFA1V0YK/7nbpxVu3JdXUxyWTKMez9lsHUtAwa0w==} + dev: true + /parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -8284,6 +8601,13 @@ packages: engines: {node: '>=8'} dev: true + /phin@3.7.0: + resolution: {integrity: sha512-DqnVNrpYhKGBZppNKprD+UJylMeEKOZxHgPB+ZP6mGzf3uA2uox4Ep9tUm+rUc8WLIdHT3HcAE4X8fhwQA9JKg==} + engines: {node: '>= 8'} + dependencies: + centra: 2.6.0 + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -8390,6 +8714,13 @@ packages: engines: {node: '>=4'} dev: true + /polka@0.5.2: + resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==} + dependencies: + '@polka/url': 0.5.0 + trouter: 2.0.1 + dev: true + /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -8417,6 +8748,15 @@ packages: hasBin: true dev: true + /pretty-format@29.6.1: + resolution: {integrity: sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.0 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /pretty-format@29.6.3: resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8491,7 +8831,6 @@ packages: /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - requiresBuild: true dev: true /pure-rand@6.0.2: @@ -8582,7 +8921,7 @@ packages: dependencies: find-up: 6.3.0 read-pkg: 8.1.0 - type-fest: 4.3.0 + type-fest: 4.3.1 dev: true /read-pkg-up@7.0.1: @@ -8640,7 +8979,7 @@ packages: '@types/normalize-package-data': 2.4.1 normalize-package-data: 6.0.0 parse-json: 7.0.0 - type-fest: 4.3.0 + type-fest: 4.3.1 dev: true /read-yaml-file@1.1.0: @@ -8700,7 +9039,7 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.3 + resolve: 1.22.2 dev: true /redent@3.0.0: @@ -8754,7 +9093,6 @@ packages: /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - requiresBuild: true dev: true /require-main-filename@2.0.0: @@ -8779,7 +9117,6 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - requiresBuild: true dev: true /resolve-from@5.0.0: @@ -8799,11 +9136,11 @@ packages: engines: {node: '>=10'} dev: true - /resolve@1.22.3: - resolution: {integrity: sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==} + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true dependencies: - is-core-module: 2.13.0 + is-core-module: 2.12.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -8890,6 +9227,16 @@ packages: tslib: 2.5.3 dev: true + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: true @@ -8948,7 +9295,7 @@ packages: cosmiconfig: 8.2.0 debug: 4.3.4 env-ci: 9.1.0 - execa: 7.2.0 + execa: 7.1.1 figures: 5.0.0 find-versions: 5.1.0 get-stream: 6.0.1 @@ -8963,7 +9310,7 @@ packages: p-reduce: 3.0.0 read-pkg-up: 9.1.0 resolve-from: 5.0.0 - semver: 7.5.4 + semver: 7.5.3 semver-diff: 4.0.0 signale: 1.4.0 yargs: 17.7.2 @@ -8976,7 +9323,7 @@ packages: resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} engines: {node: '>=12'} dependencies: - semver: 7.5.4 + semver: 7.5.3 dev: true /semver-regex@4.0.5: @@ -8993,6 +9340,11 @@ packages: hasBin: true dev: true + /semver@6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -9137,7 +9489,7 @@ packages: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} dependencies: - semver: 7.5.4 + semver: 7.5.3 dev: true /sisteransi@1.0.5: @@ -9380,7 +9732,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 dev: true /string.prototype.trimend@1.0.6: @@ -9388,7 +9740,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 dev: true /string.prototype.trimstart@1.0.6: @@ -9396,7 +9748,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.22.1 dev: true /string_decoder@1.1.1: @@ -9492,7 +9844,7 @@ packages: methods: 1.1.2 mime: 2.6.0 qs: 6.11.2 - semver: 7.5.4 + semver: 7.5.3 transitivePeerDependencies: - supports-color dev: true @@ -9588,6 +9940,30 @@ packages: engines: {node: '>=8'} dev: true + /terser-webpack-plugin@5.3.9(webpack@5.88.1): + resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.18 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.1 + terser: 5.18.0 + webpack: 5.88.1 + dev: true + /terser-webpack-plugin@5.3.9(webpack@5.88.2): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} @@ -9732,8 +10108,15 @@ packages: engines: {node: '>=8'} dev: true - /ts-api-utils@1.0.1(typescript@5.2.2): - resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} + /trouter@2.0.1: + resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==} + engines: {node: '>=6'} + dependencies: + matchit: 1.1.0 + dev: true + + /ts-api-utils@1.0.2(typescript@5.2.2): + resolution: {integrity: sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' @@ -9746,7 +10129,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.6.0 dev: true optional: true @@ -9784,7 +10167,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-loader@9.4.4(typescript@5.2.2)(webpack@5.88.2): + /ts-loader@9.4.4(typescript@5.2.2)(webpack@5.88.1): resolution: {integrity: sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==} engines: {node: '>=12.0.0'} peerDependencies: @@ -9796,7 +10179,7 @@ packages: micromatch: 4.0.5 semver: 7.5.3 typescript: 5.2.2 - webpack: 5.88.2 + webpack: 5.88.1 dev: true /ts-node@10.9.1(@types/node@18.17.14)(typescript@5.2.2): @@ -9971,8 +10354,8 @@ packages: engines: {node: '>=14.16'} dev: true - /type-fest@4.3.0: - resolution: {integrity: sha512-XbMcLhoaaX/vw1S8jTKysTlznqSPxDXj1Jf56neDMksT1xoKr02pFAhHhDbW9bFejktlwKto18/UsdXlnUCBMg==} + /type-fest@4.3.1: + resolution: {integrity: sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==} engines: {node: '>=16'} dev: true @@ -9984,6 +10367,36 @@ packages: mime-types: 2.1.35 dev: true + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.10 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.10 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.10 + dev: true + /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: @@ -10167,6 +10580,46 @@ packages: engines: {node: '>=10.13.0'} dev: true + /webpack@5.88.1: + resolution: {integrity: sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.4 + '@types/estree': 1.0.1 + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/wasm-edit': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + acorn: 8.9.0 + acorn-import-assertions: 1.9.0(acorn@8.9.0) + browserslist: 4.21.9 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.3.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.9(webpack@5.88.1) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + /webpack@5.88.2: resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} engines: {node: '>=10.13.0'} @@ -10241,6 +10694,17 @@ packages: path-exists: 4.0.0 dev: true + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + /which-typed-array@1.1.9: resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} engines: {node: '>= 0.4'} diff --git a/src/index.ts b/src/index.ts index 678b61f8..bbbc1d51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,3 +6,4 @@ export * from './throttler.guard'; export * from './throttler.module'; export { getOptionsToken, getStorageToken } from './throttler.providers'; export * from './throttler.service'; +export * from './utilities'; diff --git a/src/throttler-module-options.interface.ts b/src/throttler-module-options.interface.ts index de4c31bf..7c6d99d7 100644 --- a/src/throttler-module-options.interface.ts +++ b/src/throttler-module-options.interface.ts @@ -1,16 +1,24 @@ import { ExecutionContext, ModuleMetadata, Type } from '@nestjs/common/interfaces'; +import { ThrottlerStorage } from './throttler-storage.interface'; /** * @publicApi */ -export interface ThrottlerModuleOptions { +export interface ThrottlerOptions { + /** + * The name for the rate limit to be used. + * This can be left blank and it will be tracked as "default" internally. + * If this is set, it will be added to the return headers. + * e.g. x-ratelimit-remaining-long: 5 + */ + name?: string; /** * The amount of requests that are allowed within the ttl's time window. */ limit?: number; /** - * The amount of seconds of how many requests are allowed within this time. + * The number of milliseconds the limit of requests are allowed */ ttl?: number; @@ -19,11 +27,6 @@ export interface ThrottlerModuleOptions { */ ignoreUserAgents?: RegExp[]; - /** - * The storage class to use where all the record will be stored in. - */ - storage?: any; - /** * A factory method to determine if throttling should be skipped. * This can be based on the incoming context, or something like an env value. @@ -31,6 +34,31 @@ export interface ThrottlerModuleOptions { skipIf?: (context: ExecutionContext) => boolean; } +/** + * @publicApi + */ +export type ThrottlerModuleOptions = + | Array + | { + /** + * A factory method to determine if throttling should be skipped. + * This can be based on the incoming context, or something like an env value. + */ + skipIf?: (context: ExecutionContext) => boolean; + /** + * The user agents that should be ignored (checked against the User-Agent header). + */ + ignoreUserAgents?: RegExp[]; + /** + * The storage class to use where all the record will be stored in. + */ + storage?: Type; + /** + * The named throttlers to use + */ + throttlers: Array; + }; + /** * @publicApi */ diff --git a/src/throttler.decorator.ts b/src/throttler.decorator.ts index 5017d6f2..b7d35eb7 100644 --- a/src/throttler.decorator.ts +++ b/src/throttler.decorator.ts @@ -1,29 +1,43 @@ import { Inject } from '@nestjs/common'; import { THROTTLER_LIMIT, THROTTLER_SKIP, THROTTLER_TTL } from './throttler.constants'; import { getOptionsToken, getStorageToken } from './throttler.providers'; +import { Resolvable } from './throttler-module-options.interface'; -function setThrottlerMetadata(target: any, limit: number, ttl: number): void { - Reflect.defineMetadata(THROTTLER_TTL, ttl, target); - Reflect.defineMetadata(THROTTLER_LIMIT, limit, target); +interface ThrottlerMethodOrControllerOptions { + limit?: Resolvable; + ttl?: Resolvable; +} + +function setThrottlerMetadata( + target: any, + options: Record, +): void { + for (const name in options) { + Reflect.defineMetadata(THROTTLER_TTL + name, options[name].ttl, target); + Reflect.defineMetadata(THROTTLER_LIMIT + name, options[name].limit, target); + } } /** * Adds metadata to the target which will be handled by the ThrottlerGuard to * handle incoming requests based on the given metadata. - * @example @Throttle(2, 10) + * @example @Throttle({ default: { limit: 2, ttl: 10 }}) + * @example @Throttle({default: { limit: () => 20, ttl: () => 60 }}) * @publicApi */ -export const Throttle = (limit = 20, ttl = 60): MethodDecorator & ClassDecorator => { +export const Throttle = ( + options: Record, +): MethodDecorator & ClassDecorator => { return ( target: any, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor, ) => { if (descriptor) { - setThrottlerMetadata(descriptor.value, limit, ttl); + setThrottlerMetadata(descriptor.value, options); return descriptor; } - setThrottlerMetadata(target, limit, ttl); + setThrottlerMetadata(target, options); return target; }; }; @@ -35,18 +49,22 @@ export const Throttle = (limit = 20, ttl = 60): MethodDecorator & ClassDecorator * @example @SkipThrottle(false) * @publicApi */ -export const SkipThrottle = (skip = true): MethodDecorator & ClassDecorator => { +export const SkipThrottle = ( + skip: Record = { default: true }, +): MethodDecorator & ClassDecorator => { return ( target: any, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor, ) => { - if (descriptor) { - Reflect.defineMetadata(THROTTLER_SKIP, skip, descriptor.value); - return descriptor; + for (const key in skip) { + if (descriptor) { + Reflect.defineMetadata(THROTTLER_SKIP + key, skip[key], descriptor.value); + return descriptor; + } + Reflect.defineMetadata(THROTTLER_SKIP + key, skip[key], target); + return target; } - Reflect.defineMetadata(THROTTLER_SKIP, skip, target); - return target; }; }; diff --git a/src/throttler.guard.ts b/src/throttler.guard.ts index 37f6ec72..ba5b6ae2 100644 --- a/src/throttler.guard.ts +++ b/src/throttler.guard.ts @@ -1,7 +1,7 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import * as md5 from 'md5'; -import { ThrottlerModuleOptions } from './throttler-module-options.interface'; +import { ThrottlerModuleOptions, ThrottlerOptions } from './throttler-module-options.interface'; import { ThrottlerStorage } from './throttler-storage.interface'; import { THROTTLER_LIMIT, THROTTLER_SKIP, THROTTLER_TTL } from './throttler.constants'; import { InjectThrottlerOptions, InjectThrottlerStorage } from './throttler.decorator'; @@ -15,12 +15,36 @@ import { ThrottlerLimitDetail } from './throttler.guard.interface'; export class ThrottlerGuard implements CanActivate { protected headerPrefix = 'X-RateLimit'; protected errorMessage = throttlerMessage; + protected throttlers: Array; + protected commonOptions: Pick; constructor( @InjectThrottlerOptions() protected readonly options: ThrottlerModuleOptions, @InjectThrottlerStorage() protected readonly storageService: ThrottlerStorage, protected readonly reflector: Reflector, ) {} + async onModuleInit() { + this.throttlers = (Array.isArray(this.options) ? this.options : this.options.throttlers) + .sort((first, second) => { + if (typeof first.ttl === 'function') { + return 1; + } + if (typeof second.ttl === 'function') { + return 0; + } + return first.ttl - second.ttl; + }) + .map((opt) => ({ ...opt, name: opt.name ?? 'default' })); + if (Array.isArray(this.options)) { + this.commonOptions = {}; + } else { + this.commonOptions = { + skipIf: this.options.skipIf, + ignoreUserAgents: this.options.ignoreUserAgents, + }; + } + } + /** * Throttle requests against their TTL limit and whether to allow or deny it. * Based on the context type different handlers will be called. @@ -30,28 +54,41 @@ export class ThrottlerGuard implements CanActivate { const handler = context.getHandler(); const classRef = context.getClass(); - // Return early if the current route should be skipped. - if ( - this.reflector.getAllAndOverride(THROTTLER_SKIP, [handler, classRef]) || - this.options.skipIf?.(context) - ) { - return true; - } + const continues: boolean[] = []; + for (const namedThrottler of this.throttlers) { + // Return early if the current route should be skipped. + const skip = this.reflector.getAllAndOverride(THROTTLER_SKIP + namedThrottler.name, [ + handler, + classRef, + ]); + const skipIf = namedThrottler.skipIf || this.commonOptions.skipIf; + if (skip || skipIf?.(context)) { + continues.push(true); + continue; + } - // Return early when we have no limit or ttl data. - const routeOrClassLimit = this.reflector.getAllAndOverride(THROTTLER_LIMIT, [ - handler, - classRef, - ]); - const routeOrClassTtl = this.reflector.getAllAndOverride(THROTTLER_TTL, [ - handler, - classRef, - ]); + // Return early when we have no limit or ttl data. + const routeOrClassLimit = this.reflector.getAllAndOverride( + THROTTLER_LIMIT + namedThrottler.name, + [handler, classRef], + ); + const routeOrClassTtl = this.reflector.getAllAndOverride( + THROTTLER_TTL + namedThrottler.name, + [handler, classRef], + ); - // Check if specific limits are set at class or route level, otherwise use global options. - const limit = routeOrClassLimit || this.options.limit; - const ttl = routeOrClassTtl || this.options.ttl; - return this.handleRequest(context, limit, ttl); + // Check if specific limits are set at class or route level, otherwise use global options. + let limit = routeOrClassLimit || namedThrottler.limit; + let ttl = routeOrClassTtl || namedThrottler.ttl; + if (typeof limit === 'function') { + limit = await limit(context); + } + if (typeof ttl === 'function') { + ttl = await ttl(context); + } + continues.push(await this.handleRequest(context, limit, ttl, namedThrottler)); + } + return continues.every((cont) => cont); } /** @@ -64,25 +101,28 @@ export class ThrottlerGuard implements CanActivate { context: ExecutionContext, limit: number, ttl: number, + throttler: ThrottlerOptions, ): Promise { // Here we start to check the amount of requests being done against the ttl. const { req, res } = this.getRequestResponse(context); - + const ignoreUserAgents = throttler.ignoreUserAgents ?? this.commonOptions.ignoreUserAgents; // Return early if the current user agent should be ignored. - if (Array.isArray(this.options.ignoreUserAgents)) { - for (const pattern of this.options.ignoreUserAgents) { + if (Array.isArray(ignoreUserAgents)) { + for (const pattern of ignoreUserAgents) { if (pattern.test(req.headers['user-agent'])) { return true; } } } const tracker = this.getTracker(req); - const key = this.generateKey(context, tracker); + const key = this.generateKey(context, tracker, throttler.name); const { totalHits, timeToExpire } = await this.storageService.increment(key, ttl); + const getThrottlerSuffix = (name: string) => (name === 'default' ? '' : `-${name}`); + // Throw an error when the user reached their limit. if (totalHits > limit) { - res.header('Retry-After', timeToExpire); + res.header(`Retry-After${getThrottlerSuffix(throttler.name)}`, timeToExpire); this.throwThrottlingException(context, { limit, ttl, @@ -93,11 +133,14 @@ export class ThrottlerGuard implements CanActivate { }); } - res.header(`${this.headerPrefix}-Limit`, limit); + res.header(`${this.headerPrefix}-Limit${getThrottlerSuffix(throttler.name)}`, limit); // We're about to add a record so we need to take that into account here. // Otherwise the header says we have a request left when there are none. - res.header(`${this.headerPrefix}-Remaining`, Math.max(0, limit - totalHits)); - res.header(`${this.headerPrefix}-Reset`, timeToExpire); + res.header( + `${this.headerPrefix}-Remaining${getThrottlerSuffix(throttler.name)}`, + Math.max(0, limit - totalHits), + ); + res.header(`${this.headerPrefix}-Reset${getThrottlerSuffix(throttler.name)}`, timeToExpire); return true; } @@ -118,8 +161,8 @@ export class ThrottlerGuard implements CanActivate { * Generate a hashed key that will be used as a storage key. * The key will always be a combination of the current context and IP. */ - protected generateKey(context: ExecutionContext, suffix: string): string { - const prefix = `${context.getClass().name}-${context.getHandler().name}`; + protected generateKey(context: ExecutionContext, suffix: string, name: string): string { + const prefix = `${context.getClass().name}-${context.getHandler().name}-${name}`; return md5(`${prefix}-${suffix}`); } diff --git a/src/throttler.module.ts b/src/throttler.module.ts index 935e886e..7116ce81 100644 --- a/src/throttler.module.ts +++ b/src/throttler.module.ts @@ -16,7 +16,7 @@ export class ThrottlerModule { /** * Register the module synchronously. */ - static forRoot(options: ThrottlerModuleOptions = {}): DynamicModule { + static forRoot(options: ThrottlerModuleOptions = [{}]): DynamicModule { const providers = [...createThrottlerProviders(options), ThrottlerStorageProvider]; return { module: ThrottlerModule, diff --git a/src/throttler.providers.ts b/src/throttler.providers.ts index aecb7558..6b42f7d4 100644 --- a/src/throttler.providers.ts +++ b/src/throttler.providers.ts @@ -16,7 +16,9 @@ export function createThrottlerProviders(options: ThrottlerModuleOptions): Provi export const ThrottlerStorageProvider = { provide: ThrottlerStorage, useFactory: (options: ThrottlerModuleOptions) => { - return options.storage ? options.storage : new ThrottlerStorageService(); + return !Array.isArray(options) && options.storage + ? options.storage + : new ThrottlerStorageService(); }, inject: [THROTTLER_OPTIONS], }; diff --git a/src/throttler.service.ts b/src/throttler.service.ts index 7bf18477..7f79da44 100644 --- a/src/throttler.service.ts +++ b/src/throttler.service.ts @@ -35,7 +35,7 @@ export class ThrottlerStorageService implements ThrottlerStorage, OnApplicationS } async increment(key: string, ttl: number): Promise { - const ttlMilliseconds = ttl * 1000; + const ttlMilliseconds = ttl; if (!this.storage[key]) { this.storage[key] = { totalHits: 0, expiresAt: Date.now() + ttlMilliseconds }; } diff --git a/src/utilities.ts b/src/utilities.ts new file mode 100644 index 00000000..ff980d15 --- /dev/null +++ b/src/utilities.ts @@ -0,0 +1,5 @@ +export const seconds = (howMany: number) => howMany * 1000; +export const minutes = (howMany: number) => seconds(howMany) * 60; +export const hours = (howMany: number) => minutes(howMany) * 60; +export const days = (howMany: number) => hours(howMany) * 24; +export const weeks = (howMany: number) => days(howMany) * 7; diff --git a/test/app/controllers/app.controller.ts b/test/app/controllers/app.controller.ts index 4a322c5c..5fd17f29 100644 --- a/test/app/controllers/app.controller.ts +++ b/test/app/controllers/app.controller.ts @@ -1,9 +1,9 @@ import { Controller, Get } from '@nestjs/common'; -import { SkipThrottle, Throttle } from '../../../src'; +import { SkipThrottle, Throttle, seconds } from '../../../src'; import { AppService } from '../app.service'; @Controller() -@Throttle(2, 10) +@Throttle({ default: { limit: 2, ttl: seconds(10) } }) export class AppController { constructor(private readonly appService: AppService) {} diff --git a/test/app/controllers/controller.module.ts b/test/app/controllers/controller.module.ts index 36cc4bab..cd6e8034 100644 --- a/test/app/controllers/controller.module.ts +++ b/test/app/controllers/controller.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ThrottlerModule } from '../../../src'; +import { ThrottlerModule, seconds } from '../../../src'; import { AppService } from '../app.service'; import { AppController } from './app.controller'; import { DefaultController } from './default.controller'; @@ -7,11 +7,13 @@ import { LimitController } from './limit.controller'; @Module({ imports: [ - ThrottlerModule.forRoot({ - limit: 5, - ttl: 60, - ignoreUserAgents: [/throttler-test/g], - }), + ThrottlerModule.forRoot([ + { + limit: 5, + ttl: seconds(60), + ignoreUserAgents: [/throttler-test/g], + }, + ]), ], controllers: [AppController, DefaultController, LimitController], providers: [AppService], diff --git a/test/app/controllers/limit.controller.ts b/test/app/controllers/limit.controller.ts index c45d4cad..857e2bb6 100644 --- a/test/app/controllers/limit.controller.ts +++ b/test/app/controllers/limit.controller.ts @@ -1,8 +1,8 @@ import { Controller, Get } from '@nestjs/common'; -import { Throttle } from '../../../src'; +import { Throttle, seconds } from '../../../src'; import { AppService } from '../app.service'; -@Throttle(2, 10) +@Throttle({ default: { limit: 2, ttl: seconds(10) } }) @Controller('limit') export class LimitController { constructor(private readonly appService: AppService) {} @@ -11,7 +11,7 @@ export class LimitController { return this.appService.success(); } - @Throttle(5, 10) + @Throttle({ default: { limit: 5, ttl: seconds(10) } }) @Get('higher') getHigher() { return this.appService.success(); diff --git a/test/controller.e2e-spec.ts b/test/controller.e2e-spec.ts index b17a24a1..d957e5b4 100644 --- a/test/controller.e2e-spec.ts +++ b/test/controller.e2e-spec.ts @@ -131,10 +131,12 @@ describe('SkipIf suite', () => { ], }) .overrideProvider(THROTTLER_OPTIONS) - .useValue({ - skipIf: () => true, - limit: 5, - }) + .useValue([ + { + skipIf: () => true, + limit: 5, + }, + ]) .compile(); const app = moduleFixture.createNestApplication(); diff --git a/test/multi/app.module.ts b/test/multi/app.module.ts new file mode 100644 index 00000000..88a0a1c8 --- /dev/null +++ b/test/multi/app.module.ts @@ -0,0 +1,33 @@ +import { Module } from '@nestjs/common'; +import { APP_GUARD } from '@nestjs/core'; +import { ThrottlerGuard, ThrottlerModule, seconds, minutes } from '../../src'; +import { MultiThrottlerController } from './multi-throttler.controller'; + +@Module({ + imports: [ + ThrottlerModule.forRoot([ + { + ttl: seconds(5), + limit: 2, + }, + { + name: 'long', + ttl: minutes(1), + limit: 5, + }, + { + name: 'short', + limit: 1, + ttl: seconds(1), + }, + ]), + ], + controllers: [MultiThrottlerController], + providers: [ + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, + ], +}) +export class MultiThrottlerAppModule {} diff --git a/test/multi/multi-throttler.controller.ts b/test/multi/multi-throttler.controller.ts new file mode 100644 index 00000000..f81a1524 --- /dev/null +++ b/test/multi/multi-throttler.controller.ts @@ -0,0 +1,22 @@ +import { Controller, Get } from '@nestjs/common'; +import { SkipThrottle } from '../../src'; + +@Controller() +export class MultiThrottlerController { + @Get() + simpleRoute() { + return { success: true }; + } + + @SkipThrottle({ short: true }) + @Get('skip-short') + skipShort() { + return { success: true }; + } + + @SkipThrottle({ default: true, long: true }) + @Get('skip-default-and-long') + skipDefAndLong() { + return { success: true }; + } +} diff --git a/test/multi/multi-throttler.e2e-spec.ts b/test/multi/multi-throttler.e2e-spec.ts new file mode 100644 index 00000000..440d11fd --- /dev/null +++ b/test/multi/multi-throttler.e2e-spec.ts @@ -0,0 +1,92 @@ +import { INestApplication, Type } from '@nestjs/common'; +import { AbstractHttpAdapter } from '@nestjs/core'; +import { ExpressAdapter } from '@nestjs/platform-express'; +import { FastifyAdapter } from '@nestjs/platform-fastify'; +import { Test } from '@nestjs/testing'; +import { setTimeout } from 'node:timers/promises'; +import { request, spec } from 'pactum'; +import { MultiThrottlerAppModule } from './app.module'; + +jest.setTimeout(10000); + +const commonHeader = (prefix: string, name?: string) => `${prefix}${name ? '-' + name : ''}`; + +const remainingHeader = (name?: string) => commonHeader('x-ratelimit-remaining', name); +const limitHeader = (name?: string) => commonHeader('x-ratelimit-limit', name); +const retryHeader = (name?: string) => commonHeader('retry-after', name); + +const short = 'short'; +const long = 'long'; + +describe.each` + adapter | name + ${ExpressAdapter} | ${'express'} + ${FastifyAdapter} | ${'fastify'} +`('Mutli-Throttler Named Usage - $name', ({ adapter }: { adapter: Type }) => { + let app: INestApplication; + beforeAll(async () => { + const modRef = await Test.createTestingModule({ + imports: [MultiThrottlerAppModule], + }).compile(); + app = modRef.createNestApplication(new adapter()); + await app.listen(0); + request.setBaseUrl(await app.getUrl()); + }); + afterAll(async () => { + await app.close(); + }); + + describe('Default Route: 1/s, 2/5s, 5/min', () => { + it('should receive an exception when firing 2 request swithin a second', async () => { + await spec() + .get('/') + .expectStatus(200) + .expectHeader(remainingHeader(short), '0') + .expectHeader(limitHeader(short), '1'); + await spec().get('/').expectStatus(429).expectHeaderContains(retryHeader(short), /^\d+$/); + await setTimeout(1000); + }); + it('should get an error if we send two more requests within the first five seconds', async () => { + await spec() + .get('/') + .expectStatus(200) + .expectHeader(remainingHeader(), '0') + .expectHeader(limitHeader(), '2'); + await setTimeout(1000); + await spec().get('/').expectStatus(429).expectHeaderContains(retryHeader(), /^\d+$/); + await setTimeout(5000); + }); + it('should get an error if we smartly send 4 more requests within the minute', async () => { + await spec() + .get('/') + .expectStatus(200) + .expectHeader(limitHeader(long), '5') + .expectHeader(remainingHeader(long), '2') + .expectHeader(remainingHeader(short), '0'); + await setTimeout(1000); + await spec().get('/').expectStatus(200).expectHeader(remainingHeader(), '0'); + console.log('waiting 5 seconds'); + await setTimeout(5000); + await spec() + .get('/') + .expectStatus(200) + .expectHeader(remainingHeader(long), '0') + .expectHeader(remainingHeader(short), '0') + .expectHeader(remainingHeader(), '1'); + await setTimeout(1000); + await spec().get('/').expectStatus(429).expectHeaderContains(retryHeader(long), /^\d+$/); + }); + }); + describe('skips', () => { + it('should skip theshort throttler', async () => { + await spec().get('/skip-short').expectStatus(200).expectHeader(remainingHeader(), '1'); + await spec().get('/skip-short').expectStatus(200).expectHeader(remainingHeader(), '0'); + }); + it('should skip the default and long trackers', async () => { + await spec() + .get('/skip-default-and-long') + .expectStatus(200) + .expectHeader(remainingHeader(short), '0'); + }); + }); +});