From 34f61aa305e3991b08629732adf48b911f6bc72d Mon Sep 17 00:00:00 2001 From: Lawrence <34475808+acodeninja@users.noreply.github.com> Date: Sat, 3 Aug 2024 13:03:47 +0100 Subject: [PATCH] feat(engine): add HTTP engine (#5) * test: remove short-circuit test from called assertion * test: only run pre-commit checks against staged files * feat(engine): add HTTP engine * fix: vulnerability in fast-xml-parser * test: add semicolon required rule to eslint --- .husky/post-commit | 1 + .husky/pre-commit | 1 + eslint.config.js | 1 + package-lock.json | 549 +++++++++++++----------- src/engine/Engine.js | 26 +- src/engine/HTTPEngine.js | 175 ++++++++ src/engine/HTTPEngine.test.js | 772 ++++++++++++++++++++++++++++++++++ src/type/Model.js | 2 +- src/type/complex/ArrayType.js | 4 +- src/type/resolved/SlugType.js | 2 +- test/assertions.js | 4 - test/mocks/fetch.js | 89 ++++ test/mocks/fs.js | 10 +- test/mocks/s3.js | 6 +- 14 files changed, 1383 insertions(+), 259 deletions(-) create mode 100644 .husky/post-commit create mode 100644 src/engine/HTTPEngine.js create mode 100644 src/engine/HTTPEngine.test.js create mode 100644 test/mocks/fetch.js diff --git a/.husky/post-commit b/.husky/post-commit new file mode 100644 index 0000000..cc12031 --- /dev/null +++ b/.husky/post-commit @@ -0,0 +1 @@ +git stash apply diff --git a/.husky/pre-commit b/.husky/pre-commit index 6c35cb9..2b60993 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ +git stash --keep-index --include-untracked make commit/pre diff --git a/eslint.config.js b/eslint.config.js index 93ab7a3..70b3aeb 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -17,6 +17,7 @@ export default [ varsIgnorePattern: '^_', }], 'quote-props': ['error', 'as-needed'], + semi: ['error', 'always'], 'sort-imports': ['error', {}], quotes: ['error', 'single'], }, diff --git a/package-lock.json b/package-lock.json index d99d2d9..55bce72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -114,6 +115,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -129,6 +131,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -141,6 +144,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", @@ -154,6 +158,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", @@ -167,6 +172,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -236,66 +242,67 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.614.0.tgz", - "integrity": "sha512-9BlhfeBegvyjOqHtcr9kvrT80wiy7EVUiqYyTFiiDv/hJIcG88XHQCZdLU7658XBkQ7aFrr5b8rF2HRD1oroxw==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.622.0.tgz", + "integrity": "sha512-2lpvuQn/qymQPfwR2SxLyRy/Wi/RrEYpbQyoc9SYfhartw9TBY8c34yZkd8zNU7Y/KG3h+PLrCmNpncocuB3YA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/client-sts": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-bucket-endpoint": "3.614.0", - "@aws-sdk/middleware-expect-continue": "3.609.0", - "@aws-sdk/middleware-flexible-checksums": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.622.0", + "@aws-sdk/client-sts": "3.622.0", + "@aws-sdk/core": "3.622.0", + "@aws-sdk/credential-provider-node": "3.622.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-location-constraint": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-sdk-s3": "3.614.0", - "@aws-sdk/middleware-signing": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.622.0", + "@aws-sdk/middleware-signing": "3.620.0", "@aws-sdk/middleware-ssec": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.622.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/xml-builder": "3.609.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/eventstream-serde-browser": "^3.0.4", + "@smithy/core": "^2.3.2", + "@smithy/eventstream-serde-browser": "^3.0.5", "@smithy/eventstream-serde-config-resolver": "^3.0.3", "@smithy/eventstream-serde-node": "^3.0.4", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-blob-browser": "^3.1.2", "@smithy/hash-node": "^3.0.3", "@smithy/hash-stream-node": "^3.1.2", "@smithy/invalid-dependency": "^3.0.3", "@smithy/md5-js": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "@smithy/util-utf8": "^3.0.0", "@smithy/util-waiter": "^3.1.2", "tslib": "^2.6.2" @@ -305,44 +312,45 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.614.0.tgz", - "integrity": "sha512-p5pyYaxRzBttjBkqfc8i3K7DzBdTg3ECdVgBo6INIUxfvDy0J8QUE8vNtCgvFIkq+uPw/8M+Eo4zzln7anuO0Q==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.622.0.tgz", + "integrity": "sha512-DJwUqVR/O2lImbktUHOpaQ8XElNBx3JmWzTT2USg6jh3ErgG1CS6LIV+VUlgtxGl+tFN/G6AcAV8SdnnGydB8Q==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.622.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -354,45 +362,46 @@ } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.614.0.tgz", - "integrity": "sha512-BI1NWcpppbHg/28zbUg54dZeckork8BItZIcjls12vxasy+p3iEzrJVG60jcbUTTsk3Qc1tyxNfrdcVqx0y7Ww==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.622.0.tgz", + "integrity": "sha512-dwWDfN+S98npeY77Ugyv8VIHKRHN+n/70PWE4EgolcjaMrTINjvUh9a/SypFEs5JmBOAeCQt8S2QpM3Wvzp+pQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/core": "3.622.0", + "@aws-sdk/credential-provider-node": "3.622.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -403,50 +412,51 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.614.0" + "@aws-sdk/client-sts": "^3.622.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.614.0.tgz", - "integrity": "sha512-i6QmaVA1KHHYNnI2VYQy/sc31rLm4+jSp8b/YbQpFnD0w3aXsrEEHHlxek45uSkHb4Nrj1omFBVy/xp1WVYx2Q==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.622.0.tgz", + "integrity": "sha512-Yqtdf/wn3lcFVS42tR+zbz4HLyWxSmztjVW9L/yeMlvS7uza5nSkWqP/7ca+RxZnXLyrnA4jJtSHqykcErlhyg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.614.0", - "@aws-sdk/core": "3.614.0", - "@aws-sdk/credential-provider-node": "3.614.0", - "@aws-sdk/middleware-host-header": "3.609.0", + "@aws-sdk/client-sso-oidc": "3.622.0", + "@aws-sdk/core": "3.622.0", + "@aws-sdk/credential-provider-node": "3.622.0", + "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.609.0", - "@aws-sdk/middleware-user-agent": "3.614.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.2.6", - "@smithy/fetch-http-handler": "^3.2.1", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.3", - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.2", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.9", - "@smithy/util-defaults-mode-node": "^3.0.9", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -458,17 +468,20 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.614.0.tgz", - "integrity": "sha512-BUuS5/1YkgmKc4J0bg83XEtMyDHVyqG2QDzfmhYe8gbOIZabUl1FlrFVwhCAthtrrI6MPGTQcERB4BtJKUSplw==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.622.0.tgz", + "integrity": "sha512-q1Ct2AjPxGtQBKtDpqm1umu3f4cuWMnEHTuDa6zjjaj+Aq/C6yxLgZJo9SlcU0tMl8rUCN7oFonszfTtp4Y0MA==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/core": "^2.2.6", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/core": "^2.3.2", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", - "fast-xml-parser": "4.2.5", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { @@ -476,9 +489,10 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.609.0.tgz", - "integrity": "sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -491,19 +505,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.614.0.tgz", - "integrity": "sha512-YIEjlNUKb3Vo/iTnGAPdsiDC3FUUnNoex2OwU8LmR7AkYZiWdB8nx99DfgkkY+OFMUpw7nKD2PCOtuFONelfGA==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.622.0.tgz", + "integrity": "sha512-VUHbr24Oll1RK3WR8XLUugLpgK9ZuxEm/NVeVqyFts1Ck9gsKpRg1x4eH7L7tW3SJ4TDEQNMbD7/7J+eoL2svg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/fetch-http-handler": "^3.2.1", - "@smithy/node-http-handler": "^3.1.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.0.6", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { @@ -511,18 +526,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.614.0.tgz", - "integrity": "sha512-KfLuLFGwlvFSZ2MuzYwWGPb1y5TeiwX5okIDe0aQ1h10oD3924FXbN+mabOnUHQ8EFcGAtCaWbrC86mI7ktC6A==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.622.0.tgz", + "integrity": "sha512-cD/6O9jOfzQyo8oyAbTKnyRO89BIMSTzwaN4NxGySC6pYVTqxNSWdRwaqg/vKbwJpjbPGGYYXpXEW11kop7dlg==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.622.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.622.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -532,23 +548,24 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.614.0" + "@aws-sdk/client-sts": "^3.622.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.614.0.tgz", - "integrity": "sha512-4J6gPEuFZP0mkWq5E//oMS1vrmMM88iNNcv7TEljYnsc6JTAlKejCyFwx6CN+nkIhmIZsl06SXIhBemzBdBPfg==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.622.0.tgz", + "integrity": "sha512-keldwz4Q/6TYc37JH6m43HumN7Vi+R0AuGuHn5tBV40Vi7IiqEzjpiE+yvsHIN+duUheFLL3j/o0H32jb+14DQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.609.0", - "@aws-sdk/credential-provider-http": "3.614.0", - "@aws-sdk/credential-provider-ini": "3.614.0", - "@aws-sdk/credential-provider-process": "3.614.0", - "@aws-sdk/credential-provider-sso": "3.614.0", - "@aws-sdk/credential-provider-web-identity": "3.609.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.622.0", + "@aws-sdk/credential-provider-ini": "3.622.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.622.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", @@ -559,9 +576,10 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.614.0.tgz", - "integrity": "sha512-Q0SI0sTRwi8iNODLs5+bbv8vgz8Qy2QdxbCHnPk/6Cx6LMf7i3dqmWquFbspqFRd8QiqxStrblwxrUYZi09tkA==", + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -575,12 +593,13 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.614.0.tgz", - "integrity": "sha512-55+gp0JY4451cWI1qXmVMFM0GQaBKiQpXv2P0xmd9P3qLDyeFUSEW8XPh0d2lb1ICr6x4s47ynXVdGCIv2mXMg==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.622.0.tgz", + "integrity": "sha512-zrSoBVM2JlwvkBtrcUd4J/9CrG+T+hUy9r6jwo5gonFIN3QkneR/pqpbUn/n32Zy3zlzCo2VfB31g7MjG7kJmg==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@aws-sdk/client-sso": "3.614.0", + "@aws-sdk/client-sso": "3.622.0", "@aws-sdk/token-providers": "3.614.0", "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", @@ -593,9 +612,10 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.609.0.tgz", - "integrity": "sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==", + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -607,19 +627,20 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.609.0" + "@aws-sdk/client-sts": "^3.621.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.614.0.tgz", - "integrity": "sha512-TqEY8KcZeZ0LIxXaqG9RSSNnDHvD8RAFP4Xenwsxqnyad0Yn7LgCoFwRByelJ0t54ROYL1/ETJleWE4U4TOXdg==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz", + "integrity": "sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" @@ -629,13 +650,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.609.0.tgz", - "integrity": "sha512-+zeg//mSer4JZRxOB/4mUOMUJyuYPwATnIC5moBB8P8Xe+mJaVRFy8qlCtzYNj2TycnlsBPzTK0j7P1yvDh97w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz", + "integrity": "sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -644,16 +666,17 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.614.0.tgz", - "integrity": "sha512-ZLpxVXMboDeMT7p2Kdp5m1uLVKOktkZoMgLvvbe3zbrU4Ji5IU5xVE0aa4X7H28BtuODCs6SLESnPs19bhMKlA==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz", + "integrity": "sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-sdk/types": "3.609.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -663,13 +686,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.609.0.tgz", - "integrity": "sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -695,6 +719,7 @@ "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -706,13 +731,14 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.609.0.tgz", - "integrity": "sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -721,19 +747,22 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.614.0.tgz", - "integrity": "sha512-9fJTaiuuOfFV4FqmUEhPYzrtv7JOfYpB7q65oG3uayVH4ngWHIJkjnnX79zRhNZKdPGta+XIsnZzjEghg82ngA==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.622.0.tgz", + "integrity": "sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", "@aws-sdk/util-arn-parser": "3.568.0", "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", - "@smithy/smithy-client": "^3.1.7", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, "engines": { @@ -741,15 +770,16 @@ } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.609.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.609.0.tgz", - "integrity": "sha512-2w3dBLjQVKIajYzokO4hduq8/0hSMUYHHmIo1Kdl+MSY8uwRBt12bLL6pyreobTcRMxizvn2ph/CQ9I1ST/WGQ==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.620.0.tgz", + "integrity": "sha512-gxI7rubiaanUXaLfJ4NybERa9MGPNg2Ycl/OqANsozrBnR3Pw8vqy3EuVImQOyn2pJ2IFvl8ZPoSMHf4pX56FQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "tslib": "^2.6.2" @@ -773,14 +803,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.614.0.tgz", - "integrity": "sha512-xUxh0UPQiMTG6E31Yvu6zVYlikrIcFDKljM11CaatInzvZubGTGiX0DjpqRlfGzUNsuPc/zNrKwRP2+wypgqIw==", + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", - "@smithy/protocol-http": "^4.0.3", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -792,6 +823,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -806,15 +838,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.614.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.614.0.tgz", - "integrity": "sha512-6mW3ONW4oLzxrePznYhz7sNT9ji9Am9ufLeV722tbOVS5lArBOZ6E1oPz0uYBhisUPznWKhcLRMggt7vIJWMng==", + "version": "3.622.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.622.0.tgz", + "integrity": "sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.614.0", + "@aws-sdk/middleware-sdk-s3": "3.622.0", "@aws-sdk/types": "3.609.0", - "@smithy/protocol-http": "^4.0.3", - "@smithy/signature-v4": "^3.1.2", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -826,6 +859,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -858,6 +892,7 @@ "version": "3.568.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz", "integrity": "sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -870,6 +905,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -897,6 +933,7 @@ "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -909,6 +946,7 @@ "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@aws-sdk/types": "3.609.0", @@ -2208,6 +2246,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/node-config-provider": "^3.1.4", @@ -2221,16 +2260,17 @@ } }, "node_modules/@smithy/core": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.2.8.tgz", - "integrity": "sha512-1Y0XX0Ucyg0LWTfTVLWpmvSRtFRniykUl3dQ0os1sTd03mKDudR6mVyX+2ak1phwPXx2aEWMAAdW52JNi0mc3A==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.3.2.tgz", + "integrity": "sha512-in5wwt6chDBcUv1Lw1+QzZxN9fBffi+qOixfb65yK4sDuKG7zAUO9HAFqmVzsZM3N+3tTyvZjtnDXePpvp007Q==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/middleware-endpoint": "^3.0.5", - "@smithy/middleware-retry": "^3.0.11", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.0.4", - "@smithy/smithy-client": "^3.1.9", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "tslib": "^2.6.2" @@ -2240,9 +2280,10 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.4.tgz", - "integrity": "sha512-NKyH01m97Xa5xf3pB2QOF3lnuE8RIK0hTVNU5zvZAwZU8uspYO4DHQVlK+Y5gwSrujTfHvbfd1D9UFJAc0iYKQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/node-config-provider": "^3.1.4", @@ -2323,12 +2364,13 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.2.tgz", - "integrity": "sha512-3LaWlBZObyGrOOd7e5MlacnAKEwFBmAeiW/TOj2eR9475Vnq30uS2510+tnKbxrGjROfNdOhQqGo5j3sqLT6bA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/protocol-http": "^4.0.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", @@ -2351,6 +2393,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2380,6 +2423,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2410,12 +2454,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.4.tgz", - "integrity": "sha512-wySGje/KfhsnF8YSh9hP16pZcl3C+X6zRsvSfItQGvCyte92LliilU3SD0nR7kTlxnAJwxY8vE/k4Eoezj847Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/protocol-http": "^4.0.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -2424,9 +2469,10 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.5.tgz", - "integrity": "sha512-V4acqqrh5tDxUEGVTOgf2lYMZqPQsoGntCrjrJZEeBzEzDry2d2vcI1QCXhGltXPPY+BMc6eksZMguA9fIY8vA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/middleware-serde": "^3.0.3", @@ -2442,15 +2488,16 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.11.tgz", - "integrity": "sha512-/TIRWmhwMpv99JCGuMhJPnH7ggk/Lah7s/uNDyr7faF02BxNsyD/fz9Tw7pgCf9tYOKgjimm2Qml1Aq1pbkt6g==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.14.tgz", + "integrity": "sha512-7ZaWZJOjUxa5hgmuMspyt8v/zVsh0GXYuF7OvCmdcbVa/xbnKQoYC+uYKunAqRGTkxjOyuOCw9rmFUFOqqC0eQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.0.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.1.9", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", @@ -2465,6 +2512,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2478,6 +2526,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2491,6 +2540,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/property-provider": "^3.1.3", @@ -2503,13 +2553,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.3.tgz", - "integrity": "sha512-UiKZm8KHb/JeOPzHZtRUfyaRDO1KPKPpsd7iplhiwVGOeVdkiVJ5bVe7+NhWREMOKomrDIDdSZyglvMothLg0Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.0.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/querystring-builder": "^3.0.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -2522,6 +2573,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2532,9 +2584,10 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.0.4.tgz", - "integrity": "sha512-fAA2O4EFyNRyYdFLVIv5xMMeRb+3fRKc/Rt2flh5k831vLvUmNFXcydeg7V3UeEhGURJI4c1asmGJBjvmF6j8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2548,6 +2601,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2562,6 +2616,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2575,6 +2630,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0" @@ -2587,6 +2643,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2597,12 +2654,14 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", @@ -2615,16 +2674,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.9.tgz", - "integrity": "sha512-My2RaInZ4gSwJUPMaiLR/Nk82+c4LlvqpXA+n7lonGYgCZq23Tg+/xFhgmiejJ6XPElYJysTPyV90vKyp17+1g==", + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.12.tgz", + "integrity": "sha512-wtm8JtsycthkHy1YA4zjIh2thJgIQ9vGkoR639DBx5lLlLNU0v4GARpQZkr2WjXue74nZ7MiTSWfVrLkyD8RkA==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/middleware-endpoint": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.0.4", + "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.1", + "@smithy/util-stream": "^3.1.3", "tslib": "^2.6.2" }, "engines": { @@ -2647,6 +2707,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/querystring-parser": "^3.0.3", @@ -2672,6 +2733,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -2681,6 +2743,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -2706,6 +2769,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -2715,13 +2779,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.11.tgz", - "integrity": "sha512-O3s9DGb3bmRvEKmT8RwvSWK4A9r6svfd+MnJB+UMi9ZcCkAnoRtliulOnGF0qCMkKF9mwk2tkopBBstalPY/vg==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.14.tgz", + "integrity": "sha512-0iwTgKKmAIf+vFLV8fji21Jb2px11ktKVxbX6LIDPAUJyWQqGqBVfwba7xwa1f2FZUoolYQgLvxQEpJycXuQ5w==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.9", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -2731,16 +2796,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.11.tgz", - "integrity": "sha512-qd4a9qtyOa/WY14aHHOkMafhh9z8D2QTwlcBoXMTPnEwtcY+xpe1JyFm9vya7VsB8hHsfn3XodEtwqREiu4ygQ==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.14.tgz", + "integrity": "sha512-e9uQarJKfXApkTMMruIdxHprhcXivH1flYCe8JRDTzkkLx8dA3V5J8GZlST9yfDiRWkJpZJlUXGN9Rc9Ade3OQ==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.1.4", + "@smithy/credential-provider-imds": "^3.2.0", "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.1.9", + "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" }, @@ -2752,6 +2818,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/node-config-provider": "^3.1.4", @@ -2778,6 +2845,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/types": "^3.3.0", @@ -2791,6 +2859,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "license": "Apache-2.0", "optional": true, "dependencies": { "@smithy/service-error-classification": "^3.0.3", @@ -2802,13 +2871,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.1.tgz", - "integrity": "sha512-EhRnVvl3AhoHAT2rGQ5o+oSDRM/BUSMPLZZdRJZLcNVUsFAjOs4vHaPdNQivTSzRcFxf5DA4gtO46WWU2zimaw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@smithy/fetch-http-handler": "^3.2.2", - "@smithy/node-http-handler": "^3.1.3", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", "@smithy/types": "^3.3.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", @@ -2824,6 +2894,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", "optional": true, "dependencies": { "tslib": "^2.6.2" @@ -3250,6 +3321,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT", "optional": true }, "node_modules/brace-expansion": { @@ -4803,19 +4875,20 @@ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "optional": true, "dependencies": { "strnum": "^1.0.5" @@ -10568,6 +10641,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT", "optional": true }, "node_modules/super-regex": { @@ -11079,6 +11153,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" diff --git a/src/engine/Engine.js b/src/engine/Engine.js index 3f8f3c4..70ba456 100644 --- a/src/engine/Engine.js +++ b/src/engine/Engine.js @@ -40,6 +40,7 @@ export default class Engine { } static async search(model, query) { + this._checkConfiguration?.(); const index = await this.getSearchIndexCompiled(model); try { @@ -61,12 +62,14 @@ export default class Engine { } static async find(model, parameters) { + this._checkConfiguration?.(); const response = await this.findByValue(model, parameters); return response.map(m => model.fromData(m)); } static async put(model) { + this._checkConfiguration?.(); const uploadedModels = []; const indexUpdates = {}; @@ -87,7 +90,7 @@ export default class Engine { await this.putSearchIndexRaw(model.constructor, rawSearchIndex); const compiledIndex = lunr(function () { - this.ref('id') + this.ref('id'); for (const field of model.constructor.searchProperties()) { this.field(field); @@ -95,7 +98,7 @@ export default class Engine { Object.values(rawSearchIndex).forEach(function (doc) { this.add(doc); - }, this) + }, this); }); await this.putSearchIndexCompiled(model.constructor, compiledIndex); @@ -118,6 +121,7 @@ export default class Engine { } static async get(model, id) { + this._checkConfiguration?.(); const found = await this.getById(id); try { @@ -128,6 +132,7 @@ export default class Engine { } static async hydrate(model) { + this._checkConfiguration?.(); const hydratedModels = {}; const hydrateModel = async (modelToProcess) => { @@ -142,7 +147,7 @@ export default class Engine { } return modelToProcess; - } + }; const hydrateSubModel = async (property, modelToProcess, name) => { if (hydratedModels[property.id]) { @@ -155,7 +160,7 @@ export default class Engine { const hydratedSubModel = await hydrateModel(subModel); hydratedModels[property.id] = hydratedSubModel; return hydratedSubModel; - } + }; const hydrateModelList = async (property, modelToProcess, name) => { const subModelClass = getSubModelClass(modelToProcess, name, true); @@ -177,7 +182,7 @@ export default class Engine { hydratedModels[hydratedSubModel.id] = hydratedSubModel; return hydratedSubModel; })); - } + }; function getSubModelClass(modelToProcess, name, isArray = false) { const constructorField = modelToProcess.constructor[name]; @@ -195,7 +200,7 @@ export default class Engine { static _configuration = configuration; } - Object.defineProperty(ConfiguredStore, 'name', {value: `${this.toString()}`}) + Object.defineProperty(ConfiguredStore, 'name', {value: `${this.toString()}`}); return ConfiguredStore; } @@ -213,3 +218,12 @@ export class NotFoundEngineError extends EngineError { export class NotImplementedError extends EngineError { } + +export class MissConfiguredError extends EngineError { + configuration; + + constructor(configuration) { + super('Engine is miss-configured'); + this.configuration = configuration; + } +} diff --git a/src/engine/HTTPEngine.js b/src/engine/HTTPEngine.js new file mode 100644 index 0000000..50bffe2 --- /dev/null +++ b/src/engine/HTTPEngine.js @@ -0,0 +1,175 @@ +import Engine, {MissConfiguredError} from './Engine.js'; + +export default class HTTPEngine extends Engine { + static configure(configuration = {}) { + configuration.fetchOptions = { + ...(configuration.fetchOptions ?? {}), + headers: { + ...(configuration.fetchOptions?.headers ?? {}), + Accept: 'application/json', + }, + }; + + return super.configure(configuration); + } + + static _checkConfiguration() { + if ( + !this._configuration?.host + ) throw new MissConfiguredError(this._configuration); + } + + static _getReadOptions() { + return this._configuration.fetchOptions; + } + + static _getWriteOptions() { + return { + ...this._getReadOptions(), + headers: { + ...this._getReadOptions().headers, + 'Content-Type': 'application/json', + }, + method: 'PUT', + }; + } + + static async getById(id) { + this._checkConfiguration(); + const url = new URL([ + this._configuration.host, + this._configuration.prefix, + `${id}.json`, + ].filter(e => !!e).join('/')); + + try { + return await this._configuration.fetch(url, this._getReadOptions()).then(r => r.json()); + } catch (_error) { + return undefined; + } + } + + static async findByValue(model, parameters) { + const index = await this.getIndex(model.name); + return Object.values(index) + .filter((model) => + Object.entries(parameters) + .some(([name, value]) => model[name] === value), + ); + } + + static async putModel(model) { + const url = new URL([ + this._configuration.host, + this._configuration.prefix, + `${model.id}.json`, + ].filter(e => !!e).join('/')); + + try { + return await this._configuration.fetch(url, { + ...this._getWriteOptions(), + body: JSON.stringify(model.toData()), + }).then(r => r.json()); + } catch (_error) { + return undefined; + } + } + + static async putIndex(index) { + const processIndex = async (location, models) => { + const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()])); + const url = new URL([ + this._configuration.host, + this._configuration.prefix, + location, + '_index.json', + ].filter(e => !!e).join('/')); + + const currentIndex = await this.getIndex(location); + + try { + return await this._configuration.fetch(url, { + ...this._getWriteOptions(), + body: JSON.stringify({ + ...currentIndex, + ...modelIndex, + }), + }).then(r => r.json()); + } catch (_error) { + return undefined; + } + }; + + for (const [location, models] of Object.entries(index)) { + await processIndex(location, models); + } + + await processIndex(null, Object.values(index).flat()); + } + + static async getIndex(location) { + const url = new URL(this._configuration.host + '/' + [this._configuration.prefix, location, '_index.json'].filter(e => !!e).join('/')); + + try { + return await this._configuration.fetch(url, this._getReadOptions()).then(r => r.json()); + } catch (_error) { + return {}; + } + } + + static async getSearchIndexCompiled(model) { + const url = new URL(this._configuration.host + '/' + [this._configuration.prefix].concat([model.name]).concat(['_search_index.json']).join('/')); + + try { + return await this._configuration.fetch(url, this._getReadOptions()).then(r => r.json()); + } catch (_error) { + return {}; + } + } + + static async getSearchIndexRaw(model) { + const url = new URL(this._configuration.host + '/' + [this._configuration.prefix].concat([model.name]).concat(['_search_index_raw.json']).join('/')); + + try { + return await this._configuration.fetch(url, this._getReadOptions()).then(r => r.json()); + } catch (_error) { + return {}; + } + } + + static async putSearchIndexCompiled(model, compiledIndex) { + const url = new URL([ + this._configuration.host, + this._configuration.prefix, + model.name, + '_search_index.json', + ].filter(e => !!e).join('/')); + + try { + return await this._configuration.fetch(url, { + ...this._getWriteOptions(), + body: JSON.stringify(compiledIndex), + }).then(r => r.json()); + } catch (_error) { + return undefined; + } + } + + static async putSearchIndexRaw(model, rawIndex) { + const url = new URL([ + this._configuration.host, + this._configuration.prefix, + model.name, + '_search_index_raw.json', + ].filter(e => !!e).join('/')); + + try { + return await this._configuration.fetch(url, { + ...this._getWriteOptions(), + body: JSON.stringify(rawIndex), + }).then(r => r.json()); + } catch (_error) { + return undefined; + } + } +} diff --git a/src/engine/HTTPEngine.test.js b/src/engine/HTTPEngine.test.js new file mode 100644 index 0000000..f77af5b --- /dev/null +++ b/src/engine/HTTPEngine.test.js @@ -0,0 +1,772 @@ +import {MainModel, getTestModelInstance, valid} from '../../test/fixtures/TestModel.js'; +import {MissConfiguredError, NotFoundEngineError, NotImplementedError} from './Engine.js'; +import HTTPEngine from './HTTPEngine.js'; +import assertions from '../../test/assertions.js'; +import stubFetch from '../../test/mocks/fetch.js'; +import test from 'ava'; + +test('HTTPEngine.configure(configuration) returns a new engine without altering the exising one', t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + const originalStore = HTTPEngine; + const configuredStore = originalStore.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }); + + t.is(originalStore._configuration, undefined); + t.like(configuredStore._configuration, { + host: 'https://example.com', + prefix: 'test', + fetch, + }); +}); + +test('HTTPEngine.configure(configuration) with additional headers returns a new engine with the headers', t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + const originalStore = HTTPEngine; + const configuredStore = originalStore.configure({ + host: 'https://example.com', + prefix: 'test', + fetchOptions: { + headers: {Authorization: 'Bearer some-bearer-token-for-authentication'}, + }, + fetch, + }); + + t.is(originalStore._configuration, undefined); + t.like(configuredStore._getReadOptions(), { + headers: {Authorization: 'Bearer some-bearer-token-for-authentication'}, + }); + t.like(configuredStore._getWriteOptions(), { + headers: {Authorization: 'Bearer some-bearer-token-for-authentication'}, + }); +}); + +test('HTTPEngine.get(MainModel, id) when engine is not configured', async t => { + const error = await t.throwsAsync( + async () => await HTTPEngine.get(MainModel, 'MainModel/000000000000'), + { + instanceOf: MissConfiguredError, + }, + ); + + t.is(error.message, 'Engine is miss-configured'); +}); + +test('HTTPEngine.get(MainModel, id) when id exists', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + + const model = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).get(MainModel, 'MainModel/000000000000'); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + + t.true(model instanceof MainModel); + t.true(model.validate()); + t.like(model.toData(), { + ...valid, + stringSlug: 'string', + requiredStringSlug: 'required-string', + }); +}); + +test('HTTPEngine.get(MainModel, id) when id does not exist', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + await t.throwsAsync( + () => + HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).get(MainModel, 'MainModel/999999999999'), + { + instanceOf: NotFoundEngineError, + message: 'HTTPEngine.get(MainModel/999999999999) model not found', + }, + ); + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/999999999999.json'), {headers: {Accept: 'application/json'}}); +}); + +test('HTTPEngine.put(model)', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + version: '2.3.9', + fields: ['string'], + fieldVectors: [['string/MainModel/000000000000', [0, 0.288]]], + invertedIndex: [['string', {_index: 0, string: {'MainModel/000000000000': {}}}]], + pipeline: ['stemmer'], + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.linked.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/111111111111.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.requiredLinked.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'LinkedModel/000000000000': {id: 'LinkedModel/000000000000'}, + 'LinkedModel/111111111111': {id: 'LinkedModel/111111111111'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.linkedMany[0].toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'LinkedManyModel/000000000000': {id: 'LinkedManyModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.circular.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'CircularModel/000000000000': {id: 'CircularModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.circularMany[0].toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'CircularManyModel/000000000000': {id: 'CircularManyModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + 'CircularModel/000000000000': {id: 'CircularModel/000000000000'}, + 'LinkedModel/000000000000': {id: 'LinkedModel/000000000000'}, + 'LinkedModel/111111111111': {id: 'LinkedModel/111111111111'}, + 'CircularManyModel/000000000000': {id: 'CircularManyModel/000000000000'}, + 'LinkedManyModel/000000000000': {id: 'LinkedManyModel/000000000000'}, + }), + }); +}); + +test('HTTPEngine.put(model) when getting and putting a search index fails', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)], { + 'MainModel/_search_index.json': undefined, + }); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + version: '2.3.9', + fields: ['string'], + fieldVectors: [['string/MainModel/000000000000', [0, 0.288]]], + invertedIndex: [['string', {_index: 0, string: {'MainModel/000000000000': {}}}]], + pipeline: ['stemmer'], + }), + }); +}); + +test('HTTPEngine.put(model) when getting and putting a raw search index fails', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)], { + 'MainModel/_search_index_raw.json': undefined, + }); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + }, + }), + }); +}); + +test('HTTPEngine.put(model) when getting and putting an index fails', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)], { + 'MainModel/_index.json': undefined, + }); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.toData()), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + }), + }); +}); + +test('HTTPEngine.put(model) when the initial model put fails', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)], { + 'MainModel/000000000000.json': undefined, + }); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify(model.toData()), + }); +}); + +test('HTTPEngine.put(model) updates existing search indexes', async t => { + const fetch = stubFetch({ + 'MainModel/_search_index_raw.json': { + 'MainModel/111111111111': { + id: 'MainModel/111111111111', + string: 'String', + }, + }, + }, [getTestModelInstance(valid)]); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index_raw.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/111111111111': { + id: 'MainModel/111111111111', + string: 'String', + }, + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + version: '2.3.9', + fields: ['string'], + fieldVectors: [['string/MainModel/111111111111', [0, 0.182]], ['string/MainModel/000000000000', [0, 0.182]]], + invertedIndex: [['string', { + _index: 0, + string: {'MainModel/111111111111': {}, 'MainModel/000000000000': {}}, + }]], + pipeline: ['stemmer'], + }), + }); +}); + +test('HTTPEngine.put(model) updates existing indexes', async t => { + const fetch = stubFetch({ + 'MainModel/_index.json': { + 'MainModel/111111111111': { + id: 'MainModel/111111111111', + string: 'String', + }, + }, + }, [getTestModelInstance(valid)]); + + const model = getTestModelInstance(valid); + await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).put(model); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/111111111111': { + id: 'MainModel/111111111111', + string: 'String', + }, + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'LinkedModel/000000000000': {id: 'LinkedModel/000000000000'}, + 'LinkedModel/111111111111': {id: 'LinkedModel/111111111111'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'LinkedManyModel/000000000000': {id: 'LinkedManyModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'CircularModel/000000000000': {id: 'CircularModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'CircularManyModel/000000000000': {id: 'CircularManyModel/000000000000'}, + }), + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/_index.json'), {headers: {Accept: 'application/json'}}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/_index.json'), { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'PUT', + body: JSON.stringify({ + 'MainModel/000000000000': { + id: 'MainModel/000000000000', + string: 'String', + stringSlug: 'string', + }, + 'CircularModel/000000000000': {id: 'CircularModel/000000000000'}, + 'LinkedModel/000000000000': {id: 'LinkedModel/000000000000'}, + 'LinkedModel/111111111111': {id: 'LinkedModel/111111111111'}, + 'CircularManyModel/000000000000': {id: 'CircularManyModel/000000000000'}, + 'LinkedManyModel/000000000000': {id: 'LinkedManyModel/000000000000'}, + }), + }); +}); + +test('HTTPEngine.find(MainModel, {string: "test"}) when a matching model exists', async t => { + const fetch = stubFetch({}, [getTestModelInstance(valid)]); + + const models = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).find(MainModel, {string: 'String'}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + t.like(models, [{id: 'MainModel/000000000000', string: 'String'}]); +}); + +test('HTTPEngine.find(MainModel, {string: "test"}) when a matching model does not exist', async t => { + const fetch = stubFetch({}, [getTestModelInstance({id: 'MainModel/999999999999'})]); + + const models = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).find(MainModel, {string: 'String'}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + t.deepEqual(models, []); +}); + +test('HTTPEngine.find(MainModel, {string: "test"}) when no index exists', async t => { + const fetch = stubFetch({}, []); + + const models = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).find(MainModel, {string: 'String'}); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_index.json'), {headers: {Accept: 'application/json'}}); + + t.deepEqual(models, []); +}); + +test('HTTPEngine.search(MainModel, "String") when a matching model exists', async t => { + const model0 = getTestModelInstance(valid); + const model1 = getTestModelInstance({ + id: 'MainModel/111111111111', + string: 'another string', + }); + const fetch = stubFetch({}, [model0, model1]); + + const models = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).search(MainModel, 'String'); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/111111111111.json'), {headers: {Accept: 'application/json'}}); + + t.like(models, [{ + ref: 'MainModel/000000000000', + score: 0.211, + model: model0.toData(), + }, { + ref: 'MainModel/111111111111', + score: 0.16, + model: model1.toData(), + }]); +}); + +test('HTTPEngine.search(MainModel, "not-even-close-to-a-match") when a matching model exists', async t => { + const fetch = stubFetch({}, [ + getTestModelInstance(valid), + getTestModelInstance({ + id: 'MainModel/1111111111111', + string: 'another string', + }), + ]); + + const models = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).search(MainModel, 'not-even-close-to-a-match'); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), {headers: {Accept: 'application/json'}}); + + t.deepEqual(models, []); +}); + +test('HTTPEngine.search(MainModel, "String") when no index exists for the model', async t => { + const fetch = stubFetch({}, []); + + await t.throwsAsync(async () => await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).search(MainModel, 'String'), { + instanceOf: NotImplementedError, + message: 'The model MainModel does not have a search index available.', + }); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/_search_index.json'), {headers: {Accept: 'application/json'}}); +}); + +test('HTTPEngine.hydrate(model)', async t => { + const model = getTestModelInstance(valid); + + const dryModel = new MainModel(); + dryModel.id = 'MainModel/000000000000'; + + const fetch = stubFetch({}, [ + getTestModelInstance(valid), + getTestModelInstance({ + id: 'MainModel/111111111111', + string: 'another string', + }), + ]); + + const hydratedModel = await HTTPEngine.configure({ + host: 'https://example.com', + prefix: 'test', + fetch, + }).hydrate(dryModel); + + assertions.calledWith(t, fetch, new URL('https://example.com/test/MainModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedModel/111111111111.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/LinkedManyModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + assertions.calledWith(t, fetch, new URL('https://example.com/test/CircularManyModel/000000000000.json'), {headers: {Accept: 'application/json'}}); + + t.deepEqual(hydratedModel, model); +}); diff --git a/src/type/Model.js b/src/type/Model.js index 46fe61a..ca91385 100644 --- a/src/type/Model.js +++ b/src/type/Model.js @@ -75,7 +75,7 @@ export default class Model { static _required = true; } - Object.defineProperty(Required, 'name', {value: `${this.toString()}`}) + Object.defineProperty(Required, 'name', {value: `${this.toString()}`}); return Required; } diff --git a/src/type/complex/ArrayType.js b/src/type/complex/ArrayType.js index 96158eb..58e44c7 100644 --- a/src/type/complex/ArrayType.js +++ b/src/type/complex/ArrayType.js @@ -3,7 +3,7 @@ import Type from '../Type.js'; export default class ArrayType { static of(type) { class ArrayOf extends Type { - static _type = 'array' + static _type = 'array'; static _items = type; static toString() { @@ -19,7 +19,7 @@ export default class ArrayType { } } - Object.defineProperty(Required, 'name', {value: `Required${this.toString()}Type`}) + Object.defineProperty(Required, 'name', {value: `Required${this.toString()}Type`}); return Required; } diff --git a/src/type/resolved/SlugType.js b/src/type/resolved/SlugType.js index cbd40bf..e068837 100644 --- a/src/type/resolved/SlugType.js +++ b/src/type/resolved/SlugType.js @@ -4,7 +4,7 @@ import slugify from 'slugify'; export default class SlugType extends ResolvedType { static of(property) { class SlugOf extends ResolvedType { - static _type = 'string' + static _type = 'string'; static toString() { return `SlugOf(${property})`; diff --git a/test/assertions.js b/test/assertions.js index eaa8e63..6d4d9df 100644 --- a/test/assertions.js +++ b/test/assertions.js @@ -9,10 +9,6 @@ function parseArgument(arg) { } export function calledWith(t, spy, ...args) { - const wasCalled = spy.calledWith(...args); - - if (wasCalled) return t.assert(wasCalled); - for (const call of spy.getCalls()) { const calledArguments = call.args.map(parseArgument); const expectedArguments = args.map(parseArgument); diff --git a/test/mocks/fetch.js b/test/mocks/fetch.js new file mode 100644 index 0000000..63ea957 --- /dev/null +++ b/test/mocks/fetch.js @@ -0,0 +1,89 @@ +import Model from '../../src/type/Model.js'; +import lunr from 'lunr'; +import sinon from 'sinon'; + +function stubFetch(filesystem = {}, models = [], errors = {}) { + const modelsAddedToFilesystem = []; + + function fileSystemFromModels(initialFilesystem = {}, ...models) { + for (const model of models) { + const modelIndexPath = model.id.replace(/[A-Z0-9]+$/, '_index.json'); + const searchIndexRawPath = model.id.replace(/[A-Z0-9]+$/, '_search_index_raw.json'); + + const modelIndex = initialFilesystem[modelIndexPath] || {}; + initialFilesystem[model.id + '.json'] = model.toData(); + initialFilesystem[modelIndexPath] = { + ...modelIndex, + [model.id]: model.toIndexData(), + }; + + if (model.constructor.searchProperties().length > 0) { + const searchIndex = initialFilesystem[searchIndexRawPath] || {}; + initialFilesystem[searchIndexRawPath] = { + ...searchIndex, + [model.id]: model.toSearchData(), + }; + } + + modelsAddedToFilesystem.push(model.id); + + for (const [_, value] of Object.entries(model)) { + if (Model.isModel(value) && !modelsAddedToFilesystem.includes(value.id)) { + initialFilesystem = fileSystemFromModels(initialFilesystem, value); + } + + if (Array.isArray(value)) { + for (const [_, subModel] of Object.entries(value)) { + if (Model.isModel(subModel) && !modelsAddedToFilesystem.includes(subModel.id)) { + initialFilesystem = fileSystemFromModels(initialFilesystem, subModel); + } + } + } + } + } + return initialFilesystem; + } + + const resolvedFiles = fileSystemFromModels(filesystem, ...models); + + const searchIndexes = Object.entries(resolvedFiles) + .filter(([name, _]) => name.endsWith('_search_index_raw.json')); + + if (searchIndexes.length > 0) { + for (const [name, index] of searchIndexes) { + const fields = [...new Set(Object.values(index).map(i => Object.keys(i).filter(i => i !== 'id')).flat(Infinity))]; + const compiledIndex = lunr(function () { + this.ref('id'); + + for (const field of fields) { + this.field(field); + } + + Object.values(index).forEach(function (doc) { + this.add(doc); + }, this); + }); + + resolvedFiles[name.replace('_raw', '')] = JSON.parse(JSON.stringify(compiledIndex)); + } + } + + return sinon.stub().callsFake(async (url, _opts) => { + for (const [path, value] of Object.entries(errors)) { + if ((url.pathname ?? url).endsWith(path)) { + if (value) return value; + return {status: 404, json: async () => {throw new Error();}}; + } + } + + for (const [filename, value] of Object.entries(resolvedFiles)) { + if ((url.pathname ?? url).endsWith(filename)) { + return {status: 200, json: async () => value}; + } + } + + return {status: 404, json: async () => {throw new Error();}}; + }); +} + +export default stubFetch; diff --git a/test/mocks/fs.js b/test/mocks/fs.js index f0770cb..e263e01 100644 --- a/test/mocks/fs.js +++ b/test/mocks/fs.js @@ -53,7 +53,7 @@ function stubFs(filesystem, models = []) { for (const [name, index] of searchIndexes) { const fields = [...new Set(Object.values(index).map(i => Object.keys(i).filter(i => i !== 'id')).flat(Infinity))]; const compiledIndex = lunr(function () { - this.ref('id') + this.ref('id'); for (const field of fields) { this.field(field); @@ -61,7 +61,7 @@ function stubFs(filesystem, models = []) { Object.values(index).forEach(function (doc) { this.add(doc); - }, this) + }, this); }); resolvedFiles[name.replace('_raw', '')] = compiledIndex; @@ -78,9 +78,9 @@ function stubFs(filesystem, models = []) { } } - const err = new Error(`ENOENT: no such file or directory, open '${filePath}'`) - err.code = 'EPIPE' - err.errno = -3 + const err = new Error(`ENOENT: no such file or directory, open '${filePath}'`); + err.code = 'EPIPE'; + err.errno = -3; throw err; }); diff --git a/test/mocks/s3.js b/test/mocks/s3.js index 0313f4c..d6cf38d 100644 --- a/test/mocks/s3.js +++ b/test/mocks/s3.js @@ -65,7 +65,7 @@ function stubS3Client(filesystem = {}, models = {}) { for (const [name, index] of searchIndexes) { const fields = [...new Set(Object.values(index).map(i => Object.keys(i).filter(i => i !== 'id')).flat(Infinity))]; const compiledIndex = lunr(function () { - this.ref('id') + this.ref('id'); for (const field of fields) { this.field(field); @@ -73,7 +73,7 @@ function stubS3Client(filesystem = {}, models = {}) { Object.values(index).forEach(function (doc) { this.add(doc); - }, this) + }, this); }); resolvedFiles[name.replace('_raw', '')] = compiledIndex; @@ -83,7 +83,7 @@ function stubS3Client(filesystem = {}, models = {}) { resolvedBuckets[bucket] = { ...(resolvedBuckets[bucket] || {}), ...resolvedFiles, - } + }; } const send = sinon.stub().callsFake(async (command) => {