From 60250a53c5467158ec7566649fbf119e1c076a8f Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 4 Aug 2022 21:53:11 +0000 Subject: [PATCH 01/61] Update to aws-sdk v3 and create first mocked tests Change arrow functions to regular functions in tests Added test fixtures, helpers, and coverage --- .github/workflows/build.yml | 21 + package-lock.json | 6495 ++++++++++++++----- package.json | 26 +- src/api/opensearch.js | 48 +- src/api/response/opensearch/index.js | 11 +- src/aws/environment.js | 18 +- src/aws/fetch.js | 64 +- src/handlers/hello.js | 33 - template.yaml | 16 - tests/fixtures/mocks/collection-1234.json | 12 + tests/fixtures/mocks/fileset-1234.json | 11 + tests/fixtures/mocks/missing-work-1234.json | 13 + tests/fixtures/mocks/work-1234.json | 13 + tests/test-helpers.js | 32 + tests/unit/api/opensearch.test.js | 63 +- tests/unit/aws/environment.test.js | 45 + tests/unit/test-handler.js | 22 - 17 files changed, 5017 insertions(+), 1926 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 src/handlers/hello.js create mode 100644 tests/fixtures/mocks/collection-1234.json create mode 100644 tests/fixtures/mocks/fileset-1234.json create mode 100644 tests/fixtures/mocks/missing-work-1234.json create mode 100644 tests/fixtures/mocks/work-1234.json create mode 100644 tests/test-helpers.js create mode 100644 tests/unit/aws/environment.test.js delete mode 100644 tests/unit/test-handler.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..466db3a4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +name: Digital Collections API CI +on: + - push +jobs: + build: + env: + AWS_ACCESS_KEY_ID: ci + AWS_SECRET_ACCESS_KEY: ci + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Use Node.js 16.x + uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: 'npm' + - run: npm ci + - name: Check code style + run: npm run prettier + - name: Run tests + run: npm run test:coverage diff --git a/package-lock.json b/package-lock.json index dabd618a..f19082dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1889 +1,4579 @@ { - "name": "hello_world", - "version": "1.0.0", + "name": "dc-api", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "hello_world", - "version": "1.0.0", - "license": "MIT", + "name": "dc-api", + "version": "0.1.0", + "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1" }, "devDependencies": { - "aws-sdk": "^2.1187.0", "chai": "^4.2.0", "mocha": "^9.1.4", + "nock": "^13.2.9", + "nyc": "^15.1.0", "prettier": "^2.7.1" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6.0.0" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, + "node_modules/@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" + "tslib": "^1.11.1" } }, - "node_modules/argparse": { + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-browser": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", + "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", + "dependencies": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" + "node_modules/@aws-crypto/sha256-browser/node_modules/@aws-crypto/sha256-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", + "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", + "dependencies": { + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "dependencies": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" } }, - "node_modules/aws-sdk": { - "version": "2.1187.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1187.0.tgz", - "integrity": "sha512-QcxQ3asIhH9QQnN/5JO3MaHRjwcy3/AsBzcAjPU+lHZGV0drnuDmg3ZkZuAa/mOgQ3MEi68G3gYD+481QJgnMg==", - "dev": true, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.4.19" - }, - "engines": { - "node": ">= 10.0.0" + "tslib": "^1.11.1" } }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", + "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.127.0.tgz", + "integrity": "sha512-G77FLYcl9egUoD3ZmR6TX94NMqBMeT53hBGrEE3uVUJV1CwfGKfaF007mPpRZnIB3avnJBQGEK6MrwlCfv2qAw==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "node_modules/@aws-sdk/client-sso": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.142.0.tgz", + "integrity": "sha512-Pewcpxq+wqcbB3t3s6KImBUUf+qqBNqMfd2wgQ3PdpYBjlNzrWYLHAnIT1vhIFjOGJXDi/qwF8FX7qbWNUB7Lg==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.130.0", + "@aws-sdk/fetch-http-handler": "3.131.0", + "@aws-sdk/hash-node": "3.127.0", + "@aws-sdk/invalid-dependency": "3.127.0", + "@aws-sdk/middleware-content-length": "3.127.0", + "@aws-sdk/middleware-host-header": "3.127.0", + "@aws-sdk/middleware-logger": "3.127.0", + "@aws-sdk/middleware-recursion-detection": "3.127.0", + "@aws-sdk/middleware-retry": "3.127.0", + "@aws-sdk/middleware-serde": "3.127.0", + "@aws-sdk/middleware-stack": "3.127.0", + "@aws-sdk/middleware-user-agent": "3.127.0", + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/node-http-handler": "3.127.0", + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/url-parser": "3.127.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", + "@aws-sdk/util-user-agent-browser": "3.127.0", + "@aws-sdk/util-user-agent-node": "3.127.0", + "@aws-sdk/util-utf8-browser": "3.109.0", + "@aws-sdk/util-utf8-node": "3.109.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", "dependencies": { - "fill-range": "^7.0.1" + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.130.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.130.0.tgz", + "integrity": "sha512-7dkCHHI9kRcHW6YNr9/2Ub6XkvU9Fu6H/BnlKbaKlDR8jq7QpaFhPhctOVi5D/NDpxJgALifexFne0dvo3piTw==", + "dependencies": { + "@aws-sdk/signature-v4": "3.130.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-config-provider": "3.109.0", + "@aws-sdk/util-middleware": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.127.0.tgz", + "integrity": "sha512-Ig7XhUikRBlnRTYT5JBGzWfYZp68X5vkFVIFCmsHHt/qVy0Nz9raZpmDHicdS1u67yxDkWgCPn/bNevWnM0GFg==", "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.127.0.tgz", + "integrity": "sha512-I6KlIBBzmJn/U1KikiC50PK3SspT9G5lkVLBaW5a6YfOcijqVTXfAN3kYzqhfeS0j4IgfJEwKVsjsZfmprJO5A==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/url-parser": "3.127.0", + "tslib": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12.0.0" } }, - "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.142.0.tgz", + "integrity": "sha512-joMJTxUTNmxURnVmmd7XhtwOwijMjh7z09V8o2EHQMk+ak+rhaRgqb2kTA2nO0J3SRxdO5z5SKkyQgW0d1fY9g==", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "@aws-sdk/credential-provider-env": "3.127.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/credential-provider-sso": "3.142.0", + "@aws-sdk/credential-provider-web-identity": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=4" + "node": ">= 12.0.0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.142.0.tgz", + "integrity": "sha512-JkhCKNkEhCS2vgD/qg5hJPatupNLObqts9FXiDia5CF6w8YcHLH+mWSvhUMCUGkunAOvFHDkQL1uPXfoQuJvPg==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@aws-sdk/credential-provider-env": "3.127.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/credential-provider-ini": "3.142.0", + "@aws-sdk/credential-provider-process": "3.127.0", + "@aws-sdk/credential-provider-sso": "3.142.0", + "@aws-sdk/credential-provider-web-identity": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12.0.0" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.127.0.tgz", + "integrity": "sha512-6v0m2lqkO9J5fNlTl+HjriQNIdfg8mjVST544+5y9EnC/FVmTnIz64vfHveWdNkP/fehFx7wTimNENtoSqCn3A==", "dependencies": { - "has-flag": "^4.0.0" + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.142.0.tgz", + "integrity": "sha512-jJvp/A5xrikaeL0DhjhQTvUc0+eF0hN5Nbo+nxpnUOiOOkyqs329g65NI1TmLp/OzDcqQ/8p5vj2+7ufTGEOXQ==", + "dependencies": { + "@aws-sdk/client-sso": "3.142.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": "*" + "node": ">= 12.0.0" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.127.0.tgz", + "integrity": "sha512-85ahDZnLYB3dqkW+cQ0bWt+NVqOoxomTrJoq3IC2q6muebeFrJ0pyf0JEW/RNRzBiUvvsZujzGdWifzWyQKfVg==", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">= 12.0.0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.131.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.131.0.tgz", + "integrity": "sha512-eNxmPZQX2IUeBGWHNC7eNTekWn9VIPLYEMKJbKYUBJryxuTJ7TtLeyEK5oakUjMwP1AUvWT+CV7C+8L7uG1omQ==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/querystring-builder": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "tslib": "^2.3.1" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/@aws-sdk/hash-node": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.127.0.tgz", + "integrity": "sha512-wx7DKlXdKebH4JcMsOevdsm2oDNMVm36kuMm0XWRIrFWQ/oq7OquDpEMJzWvGqWF/IfFUpb7FhAWZZpALwlcwA==", "dependencies": { - "color-name": "~1.1.4" + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 12.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.127.0.tgz", + "integrity": "sha512-bxvmtmJ6gIRfOHvh1jAPZBH2mzppEblPjEOFo4mOzXz4U3qPIxeuukCjboMnGK9QEpV2wObWcYYld0vxoRrfiA==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", + "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", "dependencies": { - "delayed-stream": "~1.0.0" + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 12.0.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.127.0.tgz", + "integrity": "sha512-AFmMaIEW3Rzg0TaKB9l/RENLowd7ZEEOpm0trYw1CgUUORWW/ydCsDT7pekPlC25CPbhUmWXCSA4xPFSYOVnDw==", "dependencies": { - "ms": "2.1.2" + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 12.0.0" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.127.0.tgz", + "integrity": "sha512-e2gTLJb5lYP9lRV7hN3rKY2l4jv8OygOoHElZJ3Z8KPZskjHelYPcQ8XbdfhSXXxC3vc/0QqN0ResFt3W3Pplg==", + "dependencies": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.127.0.tgz", + "integrity": "sha512-jMNLcZB/ECA7OfkNBLNeAlrLRehyfnUeNQJHW3kcxs9h1+6VxaF6wY+WKozszLI7/3OBzQrFHBQCfRZV7ykSLg==", "dependencies": { - "type-detect": "^4.0.0" + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=0.12" + "node": ">= 12.0.0" } }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.127.0.tgz", + "integrity": "sha512-tB6WX+Z1kUKTnn5h38XFrTCzoqPKjUZLUjN4Wb27/cbeSiTSKGAZcCXHOJm36Ukorl5arlybQTqGe689EU00Hw==", "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 12.0.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.127.0.tgz", + "integrity": "sha512-ZSvg/AyGUacWnf3i8ZbyImtiCH+NyafF8uV7bITP7JkwPrG+VdNocJZOr88GRM0c1A0jfkOf7+oq+fInPwwiNA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/service-error-classification": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-middleware": "3.127.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 12.0.0" } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.127.0.tgz", + "integrity": "sha512-xmWMYV/t9M+b9yHjqaD1noDNJJViI2QwOH7TQZ9VbbrvdVtDrFuS9Sf9He80TBCJqeHShwQN9783W1I3Pu/8kw==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": ">=0.3.1" + "node": ">= 12.0.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.127.0.tgz", + "integrity": "sha512-S1IoUE5o1vCmjsF5nIE8zlItNOM1UE+lhmZeigF7knXJ9+a6ewMB6POAj/s4eoi0wcn0eSnAGsqJCWMSUjOPLA==", + "dependencies": { + "tslib": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.127.0.tgz", + "integrity": "sha512-CHxgswoOzdkOEoIq7Oyob3Sx/4FYUv6BhUesAX7MNshaDDsTQPbSWjw5bqZDiL/gO+X/34fvqCVVpVD2GvxW/g==", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 12.0.0" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.127.0.tgz", + "integrity": "sha512-bAHkASMhLZHT1yv2TX6OJGFV9Lc3t1gKfTMEKdXM2O2YhGfSx9A/qLeJm79oDfnILWQtSS2NicxlRDI2lYGf4g==", + "dependencies": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": ">=6" + "node": ">= 12.0.0" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.127.0.tgz", + "integrity": "sha512-pyMKvheK8eDwWLgYIRsWy8wiyhsbYYcqkZQs3Eh6upI4E8iCY7eMmhWvHYCibvsO+UjsOwa4cAMOfwnv/Z9s8A==", + "dependencies": { + "@aws-sdk/abort-controller": "3.127.0", + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/querystring-builder": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "dev": true, + "node_modules/@aws-sdk/property-provider": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.127.0.tgz", + "integrity": "sha512-JxenxlTEkWfLrtJqIjaXaJzAVQbbscoCb5bNjmdud07ESLVfWRKJx2nAJdecHKYp2M5NQyqBuFhQ1ELSFYQKCA==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": ">=0.4.x" + "node": ">= 12.0.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.127.0.tgz", + "integrity": "sha512-UG83PVuKX40wilG2uRU0Fvz4OY8Bt+bSPOG776DFjwIXYzK7BwpJm9H2XI2HLhS5WxrJHhwrLBRgW6UiykMnFw==", "dependencies": { - "to-regex-range": "^5.0.1" + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.127.0.tgz", + "integrity": "sha512-tsoyp4lLPsASPDYWsezGAHD8VJsZbjUNATNAzTCFdH6p+4SKBK83Q5kfXCzxt13M+l3oKbxxIWLvS0kVQFyltQ==", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12.0.0" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.127.0.tgz", + "integrity": "sha512-Vn/Dv+PqUSepp/DzLqq0LJJD8HdPefJCnLbO5WcHCARHSGlyGlZUFEM45k/oEHpTvgMXj/ORaP3A+tLwLu0AmA==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.127.0.tgz", + "integrity": "sha512-wjZY9rnlA8SPrICUumTYicEKtK4/yKB62iadUk66hxe8MrH8JhuHH2NqIad0Pt/bK/YtNVhd3yb4pRapOeY5qQ==", "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">= 12.0.0" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.127.0.tgz", + "integrity": "sha512-S3Nn4KRTqoJsB/TbRZSWBBUrkckNMR0Juqz7bOB+wupVvddKP6IcpspSC/GX9zgJjVMV8iGisZ6AUsYsC5r+cA==", "dependencies": { - "is-callable": "^1.1.3" + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/@aws-sdk/signature-v4": { + "version": "3.130.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.130.0.tgz", + "integrity": "sha512-g5G1a1NHL2uOoFfC2zQdZcj+wbjgBQPkx6xGdtqNKf9v2kS0n6ap5JUGEaqWE02lUlmWHsoMsS73hXtzwXaBRQ==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@aws-sdk/is-array-buffer": "3.55.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-hex-encoding": "3.109.0", + "@aws-sdk/util-middleware": "3.127.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 6" + "node": ">= 12.0.0" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/@aws-sdk/smithy-client": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", + "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", + "dependencies": { + "@aws-sdk/middleware-stack": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@aws-sdk/types": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.127.0.tgz", + "integrity": "sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og==", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 12.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/@aws-sdk/url-parser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.127.0.tgz", + "integrity": "sha512-njZ7zn41JHRpNfr3BCesVXCLZE0zcWSfEdtRV0ICw0cU1FgYcKELSuY9+gLUB4ci6uc7gq7mPE8+w30FcM4QeA==", + "dependencies": { + "@aws-sdk/querystring-parser": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, + "node_modules/@aws-sdk/util-base64-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", + "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-base64-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", + "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 12.0.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", + "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", + "dependencies": { + "tslib": "^2.3.1" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", + "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", + "dependencies": { + "tslib": "^2.3.1" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 12.0.0" } }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", + "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.55.0", + "tslib": "^2.3.1" + }, "engines": { - "node": "*" + "node": ">= 12.0.0" } }, - "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", + "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "tslib": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 12.0.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", + "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.0.0" } }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", + "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@aws-sdk/config-resolver": "3.130.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 10.0.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", + "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", "dependencies": { - "is-glob": "^4.0.1" + "tslib": "^2.3.1" }, "engines": { - "node": ">= 6" + "node": ">= 12.0.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", + "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", "dependencies": { - "brace-expansion": "^1.1.7" + "tslib": "^2.3.1" }, "engines": { - "node": "*" + "node": ">= 12.0.0" } }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.127.0.tgz", + "integrity": "sha512-EwAPPed9TNqh+Wov2VStLn2NuJ/Wyt7IkZCbCsBuSNp3BFZ1V4gfwTjqtKCtB2LQgQ48MTgWgNCvrH0zjCSPGg==", + "dependencies": { + "tslib": "^2.3.1" + }, "engines": { - "node": ">=4.x" + "node": ">= 12.0.0" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", + "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", "dependencies": { - "function-bind": "^1.1.1" + "tslib": "^2.3.1" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 12.0.0" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.127.0.tgz", + "integrity": "sha512-uO2oHmJswuYKJS+GiMdYI8izhpC9M7/jFFvnAmLlTEVwpEi1VX9KePAOF+u5AaBC2kzITo/7dg141XfRHZloIQ==", + "dependencies": { + "@aws-sdk/types": "3.127.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.127.0.tgz", + "integrity": "sha512-3P/M4ZDD2qMeeoCk7TE/Mw7cG5IjB87F6BP8nI8/oHuaz7j6fsI7D49SNpyjl8JApRynZ122Ad6hwQwRj3isYw==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", + "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-node": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", + "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "@babel/highlight": "^7.18.6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", "dev": true, - "bin": { - "he": "bin/he" + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "@babel/types": "^7.18.6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "@babel/types": "^7.18.6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "@babel/types": "^7.18.6" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "color-convert": "^1.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=4" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=4" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.0" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" + "bin": { + "parser": "bin/babel-parser.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "node_modules/@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.9.0" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "sprintf-js": "~1.0.2" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">=6" } }, - "node_modules/js-yaml": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "get-func-name": "^2.0.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { - "node": ">= 0.6" + "node": ">=6.0.0" } }, - "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">=6.0.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" + "default-require-extensions": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "engines": { "node": "*" } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" } }, - "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { - "node": ">=0.4.x" + "node": ">=8" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "safe-buffer": "^5.1.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=8" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" }, - "engines": { - "node": ">= 0.4" + "bin": { + "browserslist": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", + "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } ] }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "has-flag": "^4.0.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=10" + "node": ">= 8.10.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, "engines": { - "node": ">=8.0" + "node": ">=6" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dev": true, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "safe-buffer": "~5.1.1" } }, - "node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" + "ms": "2.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" } }, - "node_modules/xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==", + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=8" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", + "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nock": { + "version": "13.2.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", + "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-browser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", + "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", + "requires": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "@aws-crypto/sha256-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", + "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", + "requires": { + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "requires": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", + "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", + "requires": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-sdk/abort-controller": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.127.0.tgz", + "integrity": "sha512-G77FLYcl9egUoD3ZmR6TX94NMqBMeT53hBGrEE3uVUJV1CwfGKfaF007mPpRZnIB3avnJBQGEK6MrwlCfv2qAw==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/client-sso": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.142.0.tgz", + "integrity": "sha512-Pewcpxq+wqcbB3t3s6KImBUUf+qqBNqMfd2wgQ3PdpYBjlNzrWYLHAnIT1vhIFjOGJXDi/qwF8FX7qbWNUB7Lg==", + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.130.0", + "@aws-sdk/fetch-http-handler": "3.131.0", + "@aws-sdk/hash-node": "3.127.0", + "@aws-sdk/invalid-dependency": "3.127.0", + "@aws-sdk/middleware-content-length": "3.127.0", + "@aws-sdk/middleware-host-header": "3.127.0", + "@aws-sdk/middleware-logger": "3.127.0", + "@aws-sdk/middleware-recursion-detection": "3.127.0", + "@aws-sdk/middleware-retry": "3.127.0", + "@aws-sdk/middleware-serde": "3.127.0", + "@aws-sdk/middleware-stack": "3.127.0", + "@aws-sdk/middleware-user-agent": "3.127.0", + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/node-http-handler": "3.127.0", + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/smithy-client": "3.142.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/url-parser": "3.127.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.55.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.142.0", + "@aws-sdk/util-defaults-mode-node": "3.142.0", + "@aws-sdk/util-user-agent-browser": "3.127.0", + "@aws-sdk/util-user-agent-node": "3.127.0", + "@aws-sdk/util-utf8-browser": "3.109.0", + "@aws-sdk/util-utf8-node": "3.109.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "requires": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + } + } + }, + "@aws-sdk/config-resolver": { + "version": "3.130.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.130.0.tgz", + "integrity": "sha512-7dkCHHI9kRcHW6YNr9/2Ub6XkvU9Fu6H/BnlKbaKlDR8jq7QpaFhPhctOVi5D/NDpxJgALifexFne0dvo3piTw==", + "requires": { + "@aws-sdk/signature-v4": "3.130.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-config-provider": "3.109.0", + "@aws-sdk/util-middleware": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.127.0.tgz", + "integrity": "sha512-Ig7XhUikRBlnRTYT5JBGzWfYZp68X5vkFVIFCmsHHt/qVy0Nz9raZpmDHicdS1u67yxDkWgCPn/bNevWnM0GFg==", + "requires": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.127.0.tgz", + "integrity": "sha512-I6KlIBBzmJn/U1KikiC50PK3SspT9G5lkVLBaW5a6YfOcijqVTXfAN3kYzqhfeS0j4IgfJEwKVsjsZfmprJO5A==", + "requires": { + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/url-parser": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.142.0.tgz", + "integrity": "sha512-joMJTxUTNmxURnVmmd7XhtwOwijMjh7z09V8o2EHQMk+ak+rhaRgqb2kTA2nO0J3SRxdO5z5SKkyQgW0d1fY9g==", + "requires": { + "@aws-sdk/credential-provider-env": "3.127.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/credential-provider-sso": "3.142.0", + "@aws-sdk/credential-provider-web-identity": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.142.0.tgz", + "integrity": "sha512-JkhCKNkEhCS2vgD/qg5hJPatupNLObqts9FXiDia5CF6w8YcHLH+mWSvhUMCUGkunAOvFHDkQL1uPXfoQuJvPg==", + "requires": { + "@aws-sdk/credential-provider-env": "3.127.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/credential-provider-ini": "3.142.0", + "@aws-sdk/credential-provider-process": "3.127.0", + "@aws-sdk/credential-provider-sso": "3.142.0", + "@aws-sdk/credential-provider-web-identity": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.127.0.tgz", + "integrity": "sha512-6v0m2lqkO9J5fNlTl+HjriQNIdfg8mjVST544+5y9EnC/FVmTnIz64vfHveWdNkP/fehFx7wTimNENtoSqCn3A==", + "requires": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.142.0.tgz", + "integrity": "sha512-jJvp/A5xrikaeL0DhjhQTvUc0+eF0hN5Nbo+nxpnUOiOOkyqs329g65NI1TmLp/OzDcqQ/8p5vj2+7ufTGEOXQ==", + "requires": { + "@aws-sdk/client-sso": "3.142.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.127.0.tgz", + "integrity": "sha512-85ahDZnLYB3dqkW+cQ0bWt+NVqOoxomTrJoq3IC2q6muebeFrJ0pyf0JEW/RNRzBiUvvsZujzGdWifzWyQKfVg==", + "requires": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.131.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.131.0.tgz", + "integrity": "sha512-eNxmPZQX2IUeBGWHNC7eNTekWn9VIPLYEMKJbKYUBJryxuTJ7TtLeyEK5oakUjMwP1AUvWT+CV7C+8L7uG1omQ==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/querystring-builder": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/hash-node": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.127.0.tgz", + "integrity": "sha512-wx7DKlXdKebH4JcMsOevdsm2oDNMVm36kuMm0XWRIrFWQ/oq7OquDpEMJzWvGqWF/IfFUpb7FhAWZZpALwlcwA==", + "requires": { + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.127.0.tgz", + "integrity": "sha512-bxvmtmJ6gIRfOHvh1jAPZBH2mzppEblPjEOFo4mOzXz4U3qPIxeuukCjboMnGK9QEpV2wObWcYYld0vxoRrfiA==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", + "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.127.0.tgz", + "integrity": "sha512-AFmMaIEW3Rzg0TaKB9l/RENLowd7ZEEOpm0trYw1CgUUORWW/ydCsDT7pekPlC25CPbhUmWXCSA4xPFSYOVnDw==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.127.0.tgz", + "integrity": "sha512-e2gTLJb5lYP9lRV7hN3rKY2l4jv8OygOoHElZJ3Z8KPZskjHelYPcQ8XbdfhSXXxC3vc/0QqN0ResFt3W3Pplg==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.127.0.tgz", + "integrity": "sha512-jMNLcZB/ECA7OfkNBLNeAlrLRehyfnUeNQJHW3kcxs9h1+6VxaF6wY+WKozszLI7/3OBzQrFHBQCfRZV7ykSLg==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.127.0.tgz", + "integrity": "sha512-tB6WX+Z1kUKTnn5h38XFrTCzoqPKjUZLUjN4Wb27/cbeSiTSKGAZcCXHOJm36Ukorl5arlybQTqGe689EU00Hw==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.127.0.tgz", + "integrity": "sha512-ZSvg/AyGUacWnf3i8ZbyImtiCH+NyafF8uV7bITP7JkwPrG+VdNocJZOr88GRM0c1A0jfkOf7+oq+fInPwwiNA==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/service-error-classification": "3.127.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-middleware": "3.127.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.127.0.tgz", + "integrity": "sha512-xmWMYV/t9M+b9yHjqaD1noDNJJViI2QwOH7TQZ9VbbrvdVtDrFuS9Sf9He80TBCJqeHShwQN9783W1I3Pu/8kw==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.127.0.tgz", + "integrity": "sha512-S1IoUE5o1vCmjsF5nIE8zlItNOM1UE+lhmZeigF7knXJ9+a6ewMB6POAj/s4eoi0wcn0eSnAGsqJCWMSUjOPLA==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.127.0.tgz", + "integrity": "sha512-CHxgswoOzdkOEoIq7Oyob3Sx/4FYUv6BhUesAX7MNshaDDsTQPbSWjw5bqZDiL/gO+X/34fvqCVVpVD2GvxW/g==", + "requires": { + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.127.0.tgz", + "integrity": "sha512-bAHkASMhLZHT1yv2TX6OJGFV9Lc3t1gKfTMEKdXM2O2YhGfSx9A/qLeJm79oDfnILWQtSS2NicxlRDI2lYGf4g==", + "requires": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/shared-ini-file-loader": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.127.0.tgz", + "integrity": "sha512-pyMKvheK8eDwWLgYIRsWy8wiyhsbYYcqkZQs3Eh6upI4E8iCY7eMmhWvHYCibvsO+UjsOwa4cAMOfwnv/Z9s8A==", + "requires": { + "@aws-sdk/abort-controller": "3.127.0", + "@aws-sdk/protocol-http": "3.127.0", + "@aws-sdk/querystring-builder": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/property-provider": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.127.0.tgz", + "integrity": "sha512-JxenxlTEkWfLrtJqIjaXaJzAVQbbscoCb5bNjmdud07ESLVfWRKJx2nAJdecHKYp2M5NQyqBuFhQ1ELSFYQKCA==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/protocol-http": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.127.0.tgz", + "integrity": "sha512-UG83PVuKX40wilG2uRU0Fvz4OY8Bt+bSPOG776DFjwIXYzK7BwpJm9H2XI2HLhS5WxrJHhwrLBRgW6UiykMnFw==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.127.0.tgz", + "integrity": "sha512-tsoyp4lLPsASPDYWsezGAHD8VJsZbjUNATNAzTCFdH6p+4SKBK83Q5kfXCzxt13M+l3oKbxxIWLvS0kVQFyltQ==", + "requires": { + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/querystring-parser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.127.0.tgz", + "integrity": "sha512-Vn/Dv+PqUSepp/DzLqq0LJJD8HdPefJCnLbO5WcHCARHSGlyGlZUFEM45k/oEHpTvgMXj/ORaP3A+tLwLu0AmA==", + "requires": { + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/service-error-classification": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.127.0.tgz", + "integrity": "sha512-wjZY9rnlA8SPrICUumTYicEKtK4/yKB62iadUk66hxe8MrH8JhuHH2NqIad0Pt/bK/YtNVhd3yb4pRapOeY5qQ==" + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.127.0.tgz", + "integrity": "sha512-S3Nn4KRTqoJsB/TbRZSWBBUrkckNMR0Juqz7bOB+wupVvddKP6IcpspSC/GX9zgJjVMV8iGisZ6AUsYsC5r+cA==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/signature-v4": { + "version": "3.130.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.130.0.tgz", + "integrity": "sha512-g5G1a1NHL2uOoFfC2zQdZcj+wbjgBQPkx6xGdtqNKf9v2kS0n6ap5JUGEaqWE02lUlmWHsoMsS73hXtzwXaBRQ==", + "requires": { + "@aws-sdk/is-array-buffer": "3.55.0", + "@aws-sdk/types": "3.127.0", + "@aws-sdk/util-hex-encoding": "3.109.0", + "@aws-sdk/util-middleware": "3.127.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/smithy-client": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", + "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", + "requires": { + "@aws-sdk/middleware-stack": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/types": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.127.0.tgz", + "integrity": "sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og==" + }, + "@aws-sdk/url-parser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.127.0.tgz", + "integrity": "sha512-njZ7zn41JHRpNfr3BCesVXCLZE0zcWSfEdtRV0ICw0cU1FgYcKELSuY9+gLUB4ci6uc7gq7mPE8+w30FcM4QeA==", + "requires": { + "@aws-sdk/querystring-parser": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-base64-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", + "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-base64-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", + "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", + "requires": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-body-length-browser": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", + "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-body-length-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", + "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-buffer-from": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", + "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", + "requires": { + "@aws-sdk/is-array-buffer": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-config-provider": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", + "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-defaults-mode-browser": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", + "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", + "requires": { + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-defaults-mode-node": { + "version": "3.142.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", + "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", + "requires": { + "@aws-sdk/config-resolver": "3.130.0", + "@aws-sdk/credential-provider-imds": "3.127.0", + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/property-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-hex-encoding": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", + "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", + "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-middleware": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.127.0.tgz", + "integrity": "sha512-EwAPPed9TNqh+Wov2VStLn2NuJ/Wyt7IkZCbCsBuSNp3BFZ1V4gfwTjqtKCtB2LQgQ48MTgWgNCvrH0zjCSPGg==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-uri-escape": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", + "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.127.0.tgz", + "integrity": "sha512-uO2oHmJswuYKJS+GiMdYI8izhpC9M7/jFFvnAmLlTEVwpEi1VX9KePAOF+u5AaBC2kzITo/7dg141XfRHZloIQ==", + "requires": { + "@aws-sdk/types": "3.127.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.127.0.tgz", + "integrity": "sha512-3P/M4ZDD2qMeeoCk7TE/Mw7cG5IjB87F6BP8nI8/oHuaz7j6fsI7D49SNpyjl8JApRynZ122Ad6hwQwRj3isYw==", + "requires": { + "@aws-sdk/node-config-provider": "3.127.0", + "@aws-sdk/types": "3.127.0", + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", + "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/util-utf8-node": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", + "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", + "requires": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "dev": true + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "dev": true + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "@babel/traverse": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", + "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.11", + "@babel/types": "^7.18.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", + "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" } }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } - } - }, - "dependencies": { + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1915,6 +4605,21 @@ "picomatch": "^2.0.4" } }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1932,30 +4637,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "aws-sdk": { - "version": "2.1187.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1187.0.tgz", - "integrity": "sha512-QcxQ3asIhH9QQnN/5JO3MaHRjwcy3/AsBzcAjPU+lHZGV0drnuDmg3ZkZuAa/mOgQ3MEi68G3gYD+481QJgnMg==", - "dev": true, - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.4.19" - } - }, "axios": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", @@ -1971,18 +4652,17 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2008,25 +4688,28 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" } }, "camelcase": { @@ -2035,6 +4718,12 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", + "dev": true + }, "chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -2093,6 +4782,12 @@ "readdirp": "~3.6.0" } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -2127,12 +4822,46 @@ "delayed-stream": "~1.0.0" } }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -2165,14 +4894,13 @@ "type-detect": "^4.0.0" } }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "strip-bom": "^4.0.0" } }, "delayed-stream": { @@ -2186,53 +4914,23 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "electron-to-chromium": { + "version": "1.4.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", + "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true }, "escalade": { "version": "3.1.1", @@ -2246,10 +4944,10 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "fill-range": { @@ -2261,6 +4959,17 @@ "to-regex-range": "^5.0.1" } }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2282,13 +4991,14 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { - "is-callable": "^1.1.3" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" } }, "form-data": { @@ -2301,41 +5011,22 @@ "mime-types": "^2.1.12" } }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -2350,26 +5041,11 @@ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, - "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true }, "glob": { "version": "7.2.0", @@ -2405,55 +5081,38 @@ "is-glob": "^4.0.1" } }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" } }, "he": { @@ -2462,10 +5121,22 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { @@ -2484,36 +5155,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2523,31 +5164,6 @@ "binary-extensions": "^2.0.0" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2560,15 +5176,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2578,114 +5185,136 @@ "is-extglob": "^2.1.1" } }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "append-transform": "^2.0.0" } }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" } }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "requires": { - "has-tostringtag": "^1.0.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" } }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -2697,6 +5326,24 @@ "argparse": "^2.0.1" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2706,6 +5353,18 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -2725,6 +5384,15 @@ "get-func-name": "^2.0.0" } }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2791,34 +5459,180 @@ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, + "nock": { + "version": "13.2.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", + "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + } + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "once": { @@ -2848,6 +5662,33 @@ "p-limit": "^3.0.2" } }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2860,34 +5701,97 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, "prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "dev": true + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, "randombytes": { @@ -2908,15 +5812,13 @@ "picomatch": "^2.2.1" } }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "es6-error": "^4.0.1" } }, "require-directory": { @@ -2925,16 +5827,37 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "serialize-javascript": { @@ -2946,17 +5869,59 @@ "randombytes": "^2.1.0" } }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2968,28 +5933,6 @@ "strip-ansi": "^6.0.1" } }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2999,6 +5942,12 @@ "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3014,6 +5963,34 @@ "has-flag": "^4.0.0" } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3023,53 +6000,46 @@ "is-number": "^7.0.0" } }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "is-typedarray": "^1.0.0" } }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "dev": true + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "which": { "version": "2.0.2", @@ -3080,32 +6050,11 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" - } + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true }, "workerpool": { "version": "6.2.0", @@ -3130,22 +6079,18 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==", - "dev": true - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 5b109f67..70fff42b 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,29 @@ { - "name": "hello_world", - "version": "1.0.0", - "description": "hello world sample for NodeJS", - "main": "hello.js", + "name": "dc-api", + "version": "2.0.0pre", + "description": "NUL Digital Collections API", "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", - "author": "SAM CLI", - "license": "MIT", + "author": "nulib", + "license": "Apache-2.0", "dependencies": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1" }, "scripts": { - "test": "mocha tests/unit/" + "prettier": "prettier -c src tests", + "prettier:fix": "prettier -cw src tests", + "test": "mocha --require tests/test-helpers --recursive tests/unit", + "test:coverage": "nyc --check-coverage true npm test" }, "devDependencies": { - "aws-sdk": "^2.1187.0", "chai": "^4.2.0", "mocha": "^9.1.4", + "nock": "^13.2.9", + "nyc": "^15.1.0", "prettier": "^2.7.1" } -} \ No newline at end of file +} diff --git a/src/api/opensearch.js b/src/api/opensearch.js index 82c81e57..d72fb139 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -1,9 +1,6 @@ -const AWS = require("aws-sdk"); +const { HttpRequest } = require("@aws-sdk/protocol-http"); const { awsFetch } = require("../aws/fetch"); -const { prefix } = require("../aws/environment"); - -const elasticsearchEndpoint = process.env.ELASTICSEARCH_ENDPOINT; -const region = process.env.AWS_REGION || "us-east-1"; +const { elasticsearchEndpoint, prefix } = require("../aws/environment"); async function getCollection(id) { return getDocument("dc-v2-collection", id); @@ -18,14 +15,7 @@ async function getWork(id) { } async function getDocument(index, id) { - const endpoint = new AWS.Endpoint(elasticsearchEndpoint); - const request = new AWS.HttpRequest(endpoint, region); - - request.method = "GET"; - request.path += prefix(index) + `/_doc/${id}`; - request.headers["host"] = elasticsearchEndpoint; - request.headers["Content-Type"] = "application/json"; - + const request = initRequest(`/${prefix(index)}/_doc/${id}`); let response = await awsFetch(request); if (response.statusCode === 200) { const body = JSON.parse(response.body); @@ -54,15 +44,33 @@ function isVisible(doc) { } } +function initRequest(path) { + const endpoint = elasticsearchEndpoint(); + + return new HttpRequest({ + method: "GET", + hostname: endpoint, + headers: { + Host: endpoint, + "Content-Type": "application/json", + }, + path: path, + }); +} + async function search(targets, body) { - const endpoint = new AWS.Endpoint(elasticsearchEndpoint); - const request = new AWS.HttpRequest(endpoint, region); + const endpoint = elasticsearchEndpoint(); - request.method = "POST"; - request.body = body; - request.path += `${targets}/_search`; - request.headers["host"] = elasticsearchEndpoint; - request.headers["Content-Type"] = "application/json"; + const request = new HttpRequest({ + method: "POST", + hostname: endpoint, + headers: { + Host: endpoint, + "Content-Type": "application/json", + }, + body: body, + path: `/${targets}/_search`, + }); return await awsFetch(request); } diff --git a/src/api/response/opensearch/index.js b/src/api/response/opensearch/index.js index 1379b88a..1d72759c 100644 --- a/src/api/response/opensearch/index.js +++ b/src/api/response/opensearch/index.js @@ -18,7 +18,12 @@ function transformOne(responseBody) { function transformMany(responseBody) { return { statusCode: 200, - body: JSON.stringify({ data: extractSource(responseBody.hits.hits), pagination: {total: responseBody.hits.total.value}, info: {}, aggregations: responseBody.aggregations }), + body: JSON.stringify({ + data: extractSource(responseBody.hits.hits), + pagination: { total: responseBody.hits.total.value }, + info: {}, + aggregations: responseBody.aggregations, + }), }; } @@ -32,11 +37,11 @@ function transformError(response) { } function extractSource(hits) { - return hits.map(hit => extractSingle(hit)) + return hits.map((hit) => extractSingle(hit)); } function extractSingle(hit) { - return hit._source + return hit._source; } module.exports = { transform }; diff --git a/src/aws/environment.js b/src/aws/environment.js index c38a864a..4c61d029 100644 --- a/src/aws/environment.js +++ b/src/aws/environment.js @@ -1,7 +1,15 @@ +function elasticsearchEndpoint() { + return process.env.ELASTICSEARCH_ENDPOINT; +} + function prefix(value) { - return [process.env.ENV_PREFIX, value] - .filter((val) => { return (val !== "") && !!val }) - .join("-"); -}; + const envPrefix = + process.env.ENV_PREFIX === "" ? undefined : process.env.ENV_PREFIX; + return [envPrefix, value].filter((val) => !!val).join("-"); +} + +function region() { + return process.env.AWS_REGION || "us-east-1"; +} -module.exports = { prefix } \ No newline at end of file +module.exports = { elasticsearchEndpoint, prefix, region }; diff --git a/src/aws/fetch.js b/src/aws/fetch.js index 18223ce5..0251bf9c 100644 --- a/src/aws/fetch.js +++ b/src/aws/fetch.js @@ -1,45 +1,33 @@ -const AWS = require("aws-sdk"); +const { defaultProvider } = require("@aws-sdk/credential-provider-node"); +const { SignatureV4 } = require("@aws-sdk/signature-v4"); +const { NodeHttpHandler } = require("@aws-sdk/node-http-handler"); +const { Sha256 } = require("@aws-crypto/sha256-browser"); +const region = require("./environment").region(); -function awsFetch(request) { - return new Promise((resolve, reject) => { - signRequest(request).then((signedRequest) => { - var client = new AWS.HttpClient(); - client.handleRequest( - signedRequest, - null, - function (response) { - let returnValue = { - statusCode: response.statusCode, - }; - let responseBody = ""; - response.on("data", function (chunk) { - responseBody += chunk; - }); - response.on("end", function (chunk) { - resolve({ ...returnValue, body: responseBody }); - }); - }, - function (error) { - console.error("Error: " + error); - reject(error); - } - ); - }); +async function awsFetch(request) { + const signer = new SignatureV4({ + credentials: defaultProvider(), + region: region, + service: "es", + sha256: Sha256, }); -} -function signRequest(request) { - return new Promise((resolve, _reject) => { - let chain = new AWS.CredentialProviderChain(); + const signedRequest = await signer.sign(request); + + const client = new NodeHttpHandler(); + const { response } = await client.handle(signedRequest); - chain.resolve((err, credentials) => { - if (err) { - console.error("Sending unsigned request: ", err); - } else { - var signer = new AWS.Signers.V4(request, "es"); - signer.addAuthorization(credentials, new Date()); - } - resolve(request); + return await new Promise((resolve, _reject) => { + let returnValue = { + statusCode: response.statusCode, + }; + let responseBody = ""; + + response.body.on("data", function (chunk) { + responseBody += chunk; + }); + response.body.on("end", function (chunk) { + resolve({ ...returnValue, body: responseBody }); }); }); } diff --git a/src/handlers/hello.js b/src/handlers/hello.js deleted file mode 100644 index 08735e32..00000000 --- a/src/handlers/hello.js +++ /dev/null @@ -1,33 +0,0 @@ -// const axios = require('axios') -// const url = 'http://checkip.amazonaws.com/'; -let response; - -/** - * - * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format - * @param {Object} event - API Gateway Lambda Proxy Input Format - * - * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html - * @param {Object} context - * - * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html - * @returns {Object} object - API Gateway Lambda Proxy Output Format - * - */ -exports.helloHandler = async (event, context) => { - try { - // const ret = await axios(url); - response = { - statusCode: 200, - body: JSON.stringify({ - message: "hello world", - // location: ret.data.trim() - }), - }; - } catch (err) { - console.log(err); - return err; - } - - return response; -}; diff --git a/template.yaml b/template.yaml index 3a0a6475..d2ceb40b 100644 --- a/template.yaml +++ b/template.yaml @@ -19,22 +19,6 @@ Parameters: Description: Index Prefix Default: "" Resources: - helloWorldFunction: - Type: AWS::Serverless::Function - Properties: - Handler: src/handlers/hello.helloHandler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Description: Says hello. - Events: - Api: - Type: Api - Properties: - RestApiId: !Ref dcApi - Path: /hello - Method: GET getCollectionByIdFunction: Type: AWS::Serverless::Function Properties: diff --git a/tests/fixtures/mocks/collection-1234.json b/tests/fixtures/mocks/collection-1234.json new file mode 100644 index 00000000..0c68fa5a --- /dev/null +++ b/tests/fixtures/mocks/collection-1234.json @@ -0,0 +1,12 @@ +{ + "_index": "dev-dc-v2-collection", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Collection", + "published": true + } +} diff --git a/tests/fixtures/mocks/fileset-1234.json b/tests/fixtures/mocks/fileset-1234.json new file mode 100644 index 00000000..e0b5bc13 --- /dev/null +++ b/tests/fixtures/mocks/fileset-1234.json @@ -0,0 +1,11 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet" + } +} diff --git a/tests/fixtures/mocks/missing-work-1234.json b/tests/fixtures/mocks/missing-work-1234.json new file mode 100644 index 00000000..6dd41a07 --- /dev/null +++ b/tests/fixtures/mocks/missing-work-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": false, + "visibility": "Public" + } +} diff --git a/tests/fixtures/mocks/work-1234.json b/tests/fixtures/mocks/work-1234.json new file mode 100644 index 00000000..f2ea0b6d --- /dev/null +++ b/tests/fixtures/mocks/work-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": true, + "visibility": "Public" + } +} diff --git a/tests/test-helpers.js b/tests/test-helpers.js new file mode 100644 index 00000000..8df51bea --- /dev/null +++ b/tests/test-helpers.js @@ -0,0 +1,32 @@ +const fs = require("fs"); +const nock = require("nock"); +const path = require("path"); + +global.helpers = { + saveEnvironment: () => { + const env = Object.assign({}, process.env); + + afterEach(function () { + process.env = env; + }); + }, + + mockIndex: () => { + const mock = nock("https://index.library.northwestern.edu"); + + beforeEach(function () { + process.env.ELASTICSEARCH_ENDPOINT = "index.library.northwestern.edu"; + }); + + afterEach(function () { + mock.removeAllListeners(); + }); + + return mock; + }, + + testFixture: (file) => { + const fixtureFile = path.join("tests/fixtures", file); + return eval(fs.readFileSync(fixtureFile)); + }, +}; diff --git a/tests/unit/api/opensearch.test.js b/tests/unit/api/opensearch.test.js index 5e5cb36a..be638c57 100644 --- a/tests/unit/api/opensearch.test.js +++ b/tests/unit/api/opensearch.test.js @@ -1,11 +1,64 @@ "use strict"; -const opensearch = require("../../src/api/opensearch"); +const opensearch = require("../../../src/api/opensearch"); const chai = require("chai"); const expect = chai.expect; -describe("getWork()", () => { - it("gets a work by its id", async () => { - +describe("getWork()", function () { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + it("gets a work by its id", async function () { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + const result = await opensearch.getWork("1234"); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(200); + expect(body._source.api_model).to.eq("Work"); + }); + + it("returns 404 Not Found for unpublished works", async function () { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const result = await opensearch.getWork("1234"); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(404); + expect(body.found).to.eq(false); + }); +}); + +describe("getFileSet()", function () { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + it("gets a fileset by its id", async function () { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-1234.json")); + + const result = await opensearch.getFileSet("1234"); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(200); + expect(body._source.api_model).to.eq("FileSet"); + }); +}); + +describe("getCollection()", function () { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + it("gets a collection by its id", async function () { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + + const result = await opensearch.getCollection("1234"); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(200); + expect(body._source.api_model).to.eq("Collection"); }); -}) \ No newline at end of file +}); diff --git a/tests/unit/aws/environment.test.js b/tests/unit/aws/environment.test.js new file mode 100644 index 00000000..aba3b717 --- /dev/null +++ b/tests/unit/aws/environment.test.js @@ -0,0 +1,45 @@ +"use strict"; + +const environment = require("../../../src/aws/environment"); + +const chai = require("chai"); +const expect = chai.expect; + +describe("environment", function () { + helpers.saveEnvironment(); + + it("returns the index endpoint", function () { + process.env.ELASTICSEARCH_ENDPOINT = "index.library.northwestern.edu"; + expect(environment.elasticsearchEndpoint()).to.eq( + "index.library.northwestern.edu" + ); + }); + + it("correctly handles an environment prefix", function () { + process.env.ENV_PREFIX = "test-env"; + expect(environment.prefix()).to.eq("test-env"); + expect(environment.prefix("name")).to.eq("test-env-name"); + }); + + it("correctly handles an empty environment prefix", function () { + process.env.ENV_PREFIX = ""; + expect(environment.prefix()).to.eq(""); + expect(environment.prefix("name")).to.eq("name"); + }); + + it("correctly handles a missing environment prefix", function () { + delete process.env.ENV_PREFIX; + expect(environment.prefix()).to.eq(""); + expect(environment.prefix("name")).to.eq("name"); + }); + + it("returns the AWS region", function () { + process.env.AWS_REGION = "my-region-1"; + expect(environment.region()).to.eq("my-region-1"); + }); + + it("returns the default region", function () { + delete process.env.AWS_REGION; + expect(environment.region()).to.eq("us-east-1"); + }); +}); diff --git a/tests/unit/test-handler.js b/tests/unit/test-handler.js deleted file mode 100644 index 82dd454e..00000000 --- a/tests/unit/test-handler.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; - -const app = require("../../src/handlers/hello.js"); -const chai = require("chai"); -const expect = chai.expect; -var event, context; - -describe("Tests index", function () { - it("verifies successful response", async () => { - const result = await app.helloHandler(event, context); - - expect(result).to.be.an("object"); - expect(result.statusCode).to.equal(200); - expect(result.body).to.be.an("string"); - - let response = JSON.parse(result.body); - - expect(response).to.be.an("object"); - expect(response.message).to.be.equal("hello world"); - // expect(response.location).to.be.an("string"); - }); -}); From 78ff0a64ca9a7bc96b7eb5d613690456ad574002 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Mon, 8 Aug 2022 21:26:52 +0000 Subject: [PATCH 02/61] Adds opensearch tests and fixtures for search() --- .../mocks/search-multiple-targets.json | 249 +++ tests/fixtures/mocks/search.json | 1411 +++++++++++++++++ tests/unit/api/opensearch.test.js | 38 + 3 files changed, 1698 insertions(+) create mode 100644 tests/fixtures/mocks/search-multiple-targets.json create mode 100644 tests/fixtures/mocks/search.json diff --git a/tests/fixtures/mocks/search-multiple-targets.json b/tests/fixtures/mocks/search-multiple-targets.json new file mode 100644 index 00000000..c8a9af7d --- /dev/null +++ b/tests/fixtures/mocks/search-multiple-targets.json @@ -0,0 +1,249 @@ +{ + "took": 3, + "timed_out": false, + "_shards": { + "total": 10, + "successful": 10, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 4331, + "relation": "eq" + }, + "max_score": 1.0, + "hits": [ + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5abdce5f-6168-4e73-8bf4-d89c2790ee4c", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Private", + "description": "This collection is a work in progress. The images are being cataloged and added to the Rob Linrothe Image Collection by Jen as of December 2021.", + "published": false, + "title": "Rob Linrothe Image Collection 2020", + "modified_date": "2021-12-03T16:55:40.945621Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.103197", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/5abdce5f-6168-4e73-8bf4-d89c2790ee4c", + "id": "5abdce5f-6168-4e73-8bf4-d89c2790ee4c", + "representative_image": {}, + "create_date": "2021-04-01T21:58:56.333389Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "35c3a71a-ce83-4410-82ec-640e01da86eb", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Press cutting books from the Dublin Gate Theatre", + "published": false, + "title": "Dublin Gate Theatre Archive", + "modified_date": "2021-06-28T18:06:19.151525Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.103729", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1410", + "api_link": "[PLACEHOLDER]/35c3a71a-ce83-4410-82ec-640e01da86eb", + "id": "35c3a71a-ce83-4410-82ec-640e01da86eb", + "representative_image": {}, + "create_date": "2021-06-22T20:53:44.226666Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "267bed1b-f808-4c51-acb1-0288378819d2", + "_score": 1.0, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Northwestern University Libraries’ Map & Atlas Collection allows access to an ever-growing selection of our cartographic resources in high resolution.", + "published": true, + "title": "Map and Atlas Collection", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.103987", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/267bed1b-f808-4c51-acb1-0288378819d2", + "id": "267bed1b-f808-4c51-acb1-0288378819d2", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.386638Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "ba35820a-525a-4cfa-8f23-4891c9f798c4", + "_score": 1.0, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Gift of the Deering Family. These sketches by Catalan artist Ramón Casas (1866-1932) document the work of an under-represented artist who was influential in modernist art circles in Barcelona and Paris along with his peers John Singer Sargent, Henri de Toulouse-Lautrec, and the young Pablo Picasso. Three bound hardcover volumes of sketches came to the Northwestern University Libraries in 1998 as part of a donation of books that had belonged to NU benefactor Charles Deering (1852-1927). Deering and Casas met in 1904 and established a lifelong friendship. At some point before the donation, a few hundred loose Casas drawings, postcards, and illustrated letters dating from 1891 to 1912 were bound into their current form. There were at least five volumes in the set; two remain in private collections.", + "published": true, + "title": "Ramón Casas Sketchbooks", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.104202", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/ba35820a-525a-4cfa-8f23-4891c9f798c4", + "id": "ba35820a-525a-4cfa-8f23-4891c9f798c4", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.438228Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "McCormick Library of Special Collections Audiovisual Collection", + "published": true, + "title": "McCormick Library of Special Collections Audiovisual Collection", + "modified_date": "2022-02-17T16:47:28.717491Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.104517", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "id": "8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.884163Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5e782839-d76a-4ee7-8931-79baa88770a8", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Private", + "description": "Sculpture from the collections of Northwestern University Libraries.", + "published": false, + "title": "Sculpture", + "modified_date": "2022-02-18T13:19:05.233977Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.104553", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/5e782839-d76a-4ee7-8931-79baa88770a8", + "id": "5e782839-d76a-4ee7-8931-79baa88770a8", + "representative_image": {}, + "create_date": "2021-04-22T16:46:43.668615Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "ec153e6d-a543-4b50-a7dc-ae5e98a27129", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Private", + "description": "Robert Greenfield Collection", + "published": true, + "title": "Robert Greenfield Collection", + "modified_date": "2022-02-07T19:24:30.395199Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.105084", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1786", + "api_link": "[PLACEHOLDER]/ec153e6d-a543-4b50-a7dc-ae5e98a27129", + "id": "ec153e6d-a543-4b50-a7dc-ae5e98a27129", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.957137Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "450b857e-2e74-491e-9ef1-2c73e49f0c16", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": ["photography"], + "visibility": "Public", + "description": "The photographs of George S. Duntley fill 8 boxes and cover the years from approximately 1899-1918. The collection consists of dry plate negatives and record Duntley’s years in Chicago and Evanston while he was attending Northwestern University Medical School. Also included are photographs taken in Bushnell, Illinois, his home town and surrounding areas.", + "published": true, + "title": "George Silas Duntley (1879-1957) Photographs, circa 1899-1918", + "modified_date": "2021-11-10T22:56:44.250402Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.105157", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1245", + "api_link": "[PLACEHOLDER]/450b857e-2e74-491e-9ef1-2c73e49f0c16", + "id": "450b857e-2e74-491e-9ef1-2c73e49f0c16", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.415488Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "3121f8ee-5265-4b19-bae3-59f96e9ac01a", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Private", + "description": "The Aldridge Collection consists of materials relating to 19th century African-American tragedian Ira Frederick Aldridge (1807-1967) and his children, mainly his daughter Amanda Christina Elizabeth Aldridge (known as Amanda Ira Aldridge, who composed under the pseudonym Montague Ring; 1866-1956).", + "published": true, + "title": "Aldridge Collection", + "modified_date": "2021-09-30T20:34:02.946609Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.141902", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1207", + "api_link": "[PLACEHOLDER]/3121f8ee-5265-4b19-bae3-59f96e9ac01a", + "id": "3121f8ee-5265-4b19-bae3-59f96e9ac01a", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.470273Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "dc70f756-8e30-4724-b50d-591843db2537", + "_score": 1.0, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Institution", + "description": "The posters within the collection relate to events held on campus, including music and dance performances, film screenings, talks, seminars, panels, book talks, auditions, career fairs, job opportunities, research studies and course openings, among others. Some events reoccur across time.", + "published": true, + "title": "Northwestern University Posters Collection", + "modified_date": "2021-10-13T21:04:45.100396Z", + "admin_email": null, + "indexed_at": "2022-07-28T15:43:05.142115", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1412", + "api_link": "[PLACEHOLDER]/dc70f756-8e30-4724-b50d-591843db2537", + "id": "dc70f756-8e30-4724-b50d-591843db2537", + "representative_image": {}, + "create_date": "2021-03-25T14:31:56.858368Z" + } + } + ] + } +} diff --git a/tests/fixtures/mocks/search.json b/tests/fixtures/mocks/search.json new file mode 100644 index 00000000..b421e1c4 --- /dev/null +++ b/tests/fixtures/mocks/search.json @@ -0,0 +1,1411 @@ +{ + "took": 7, + "timed_out": false, + "_shards": { + "total": 5, + "successful": 5, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 4199, + "relation": "eq" + }, + "max_score": 1.0, + "hits": [ + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "9a2a8af3-4d04-4bae-ad0a-711c5504be84", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T19:35:03.977639", + "rights_statement": null, + "id": "9a2a8af3-4d04-4bae-ad0a-711c5504be84", + "create_date": "2022-07-28T19:33:13.059045Z", + "identifier": [], + "box_number": [], + "thumbnail": null, + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/9a/2a/8a/f3/-4/d0/4-/4b/ae/-a/d0/a-/71/1c/55/04/be/84-manifest.json", + "work_type": "Audio", + "collection": {}, + "published": false, + "modified_date": "2022-07-28T19:33:13.466249Z", + "accession_number": "tests124124124", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": "test zip", + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/9a2a8af3-4d04-4bae-ad0a-711c5504be84", + "ark": "ark:/99999/fk4m05nh8w", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": {}, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "5ae1b239-309d-4e36-9e04-05606e27d637", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.353774", + "rights_statement": null, + "id": "5ae1b239-309d-4e36-9e04-05606e27d637", + "create_date": "2022-02-25T17:21:01.402160Z", + "identifier": [], + "box_number": [], + "thumbnail": null, + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/5a/e1/b2/39/-3/09/d-/4e/36/-9/e0/4-/05/60/6e/27/d6/37-manifest.json", + "work_type": "Video", + "collection": {}, + "published": false, + "modified_date": "2022-06-13T15:43:06.701558Z", + "accession_number": "P000_ReseedingFeb22_001", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Supplemental", + "streaming_url": null, + "mime_type": "application/gzip", + "rank": 1879048192, + "id": "ef98eb8a-842e-4055-9dd1-a6a6bbf0a536", + "label": "Qctools files", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/aa/5c/8b/63/-2/95/8-/48/e8/-8/92/a-/32/93/ea/17/a2/04/aa5c8b63-2958-48e8-892a-3293ea17a204.m3u8", + "mime_type": "video/x-m4v", + "rank": 1073741824, + "id": "aa5c8b63-2958-48e8-892a-3293ea17a204", + "label": "adfkjasld;kfjal;ks", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/posters/aa5c8b63-2958-48e8-892a-3293ea17a204", + "poster_offset": 5572 + }, + { + "role": "Access", + "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/fd/e2/e7/0c/-b/98/4-/47/27/-8/5d/b-/a9/62/d2/ad/5a/ba/fde2e70c-b984-4727-85db-a962d2ad5aba.m3u8", + "mime_type": "video/mp4", + "rank": 0, + "id": "fde2e70c-b984-4727-85db-a962d2ad5aba", + "label": "mp4", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/posters/fde2e70c-b984-4727-85db-a962d2ad5aba", + "poster_offset": 96371 + }, + { + "role": "Supplemental", + "streaming_url": null, + "mime_type": "application/octet-stream", + "rank": 1610612736, + "id": "03b93a59-f526-45bd-9017-82fd48bd0192", + "label": "MD5", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Supplemental", + "streaming_url": null, + "mime_type": "application/json", + "rank": 0, + "id": "71d50f4c-2ff6-4b77-ad5d-ca288285b4c0", + "label": "JSON", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Supplemental", + "streaming_url": null, + "mime_type": "application/octet-stream", + "rank": 1073741824, + "id": "288ad349-b363-4cc5-a114-00873f293ac9", + "label": "FRAME MD5", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Preservation", + "streaming_url": null, + "mime_type": null, + "rank": 0, + "id": "db2cdcde-4216-4d9e-9a0c-f7c03d966444", + "label": "MKV file", + "representative_image_url": null, + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": "Reseed Title", + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/5ae1b239-309d-4e36-9e04-05606e27d637", + "ark": "ark:/99999/fk4572wp3s", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": {}, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "0081d3de-60c0-41f8-80fb-9e57fa23e920", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.354660", + "rights_statement": null, + "id": "0081d3de-60c0-41f8-80fb-9e57fa23e920", + "create_date": "2022-02-25T17:21:02.183739Z", + "identifier": [], + "box_number": [], + "thumbnail": null, + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/00/81/d3/de/-6/0c/0-/41/f8/-8/0f/b-/9e/57/fa/23/e9/20-manifest.json", + "work_type": "Video", + "collection": {}, + "published": false, + "modified_date": "2022-02-25T17:21:02.333024Z", + "accession_number": "P000_ReseedingFeb22_002", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/df/53/6c/a3/-c/55/8-/4c/0d/-9/6f/9-/b9/1a/6f/8a/32/5c/df536ca3-c558-4c0d-96f9-b91a6f8a325c.m3u8", + "mime_type": "video/quicktime", + "rank": 1073741824, + "id": "df536ca3-c558-4c0d-96f9-b91a6f8a325c", + "label": "mov", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Preservation", + "streaming_url": null, + "mime_type": null, + "rank": 0, + "id": "168254b5-9a7e-42f5-b6a3-ae83210506c0", + "label": "mkv", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/76/bb/80/04/-2/45/5-/4a/16/-9/b1/9-/12/93/8d/20/48/38/76bb8004-2455-4a16-9b19-12938d204838.m3u8", + "mime_type": "video/mp4", + "rank": 0, + "id": "76bb8004-2455-4a16-9b19-12938d204838", + "label": "mp4", + "representative_image_url": null, + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/0081d3de-60c0-41f8-80fb-9e57fa23e920", + "ark": "ark:/99999/fk41g25v6m", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": {}, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "25a1b800-cab0-4200-abbb-9015842a7b29", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.473035", + "rights_statement": null, + "id": "25a1b800-cab0-4200-abbb-9015842a7b29", + "create_date": "2022-02-25T17:21:03.522406Z", + "identifier": [], + "box_number": [], + "thumbnail": null, + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/25/a1/b8/00/-c/ab/0-/42/00/-a/bb/b-/90/15/84/2a/7b/29-manifest.json", + "work_type": "Video", + "collection": {}, + "published": false, + "modified_date": "2022-02-25T17:21:03.740709Z", + "accession_number": "P000_ReseedingFeb22_007", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/a2/b2/28/c7/-6/8f/8-/45/0d/-9/e2/c-/21/f3/96/86/47/63/a2b228c7-68f8-450d-9e2c-21f396864763.m3u8", + "mime_type": "video/mp4", + "rank": 0, + "id": "a2b228c7-68f8-450d-9e2c-21f396864763", + "label": "Mp4", + "representative_image_url": null, + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/25a1b800-cab0-4200-abbb-9015842a7b29", + "ark": "ark:/99999/fk4cr79d2m", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": {}, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "2d839343-b459-4b27-83be-0bc4032588de", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.475179", + "rights_statement": null, + "id": "2d839343-b459-4b27-83be-0bc4032588de", + "create_date": "2022-06-27T21:14:37.734614Z", + "identifier": [], + "box_number": [], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702/full/!300,300/0/default.jpg", + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/2d/83/93/43/-b/45/9-/4b/27/-8/3b/e-/0b/c4/03/25/88/de-manifest.json", + "work_type": "Image", + "collection": {}, + "published": false, + "modified_date": "2022-06-27T21:14:37.900026Z", + "accession_number": "6-27_test_work_519", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "f0591ed1-6a0a-4baf-a983-4b854800a702", + "label": "label", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702", + "poster_offset": null + }, + { + "role": "Preservation", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "b3ced9b2-8c7e-4ec9-9099-a931ecbfd956", + "label": "label", + "representative_image_url": null, + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/2d839343-b459-4b27-83be-0bc4032588de", + "ark": "ark:/99999/fk4fb6kj4w", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": { + "fileSetId": "f0591ed1-6a0a-4baf-a983-4b854800a702", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "ee592440-4401-445b-8b8d-2991b2817e57", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "Herskovits Library of African Studies", + "indexed_at": "2022-07-28T15:45:31.541738", + "rights_statement": null, + "id": "ee592440-4401-445b-8b8d-2991b2817e57", + "create_date": "2021-11-29T17:03:48.699230Z", + "identifier": [], + "box_number": ["Box 84"], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89/full/!300,300/0/default.jpg", + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ee/59/24/40/-4/40/1-/44/5b/-8/b8/d-/29/91/b2/81/7e/57-manifest.json", + "work_type": "Image", + "collection": { + "id": "fd9927b0-6cd4-4aa7-bf86-ca84955980a6", + "title": "Vernon McKay (1912-1988) Papers" + }, + "published": true, + "modified_date": "2022-06-13T16:06:44.052690Z", + "accession_number": "P0413_B84_F04_009", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1360072977, + "id": "6145c11d-868a-4974-acb3-c51ae059ea59", + "label": "P0413_B84_F04_009_09_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/6145c11d-868a-4974-acb3-c51ae059ea59", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1216907400, + "id": "62418d3f-3777-424f-8a91-5030e78df746", + "label": "P0413_B84_F04_009_03_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/62418d3f-3777-424f-8a91-5030e78df746", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -787410671, + "id": "633b2e85-209e-48eb-b22d-5fbb061d0562", + "label": "P0413_B84_F04_009_05_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/633b2e85-209e-48eb-b22d-5fbb061d0562", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 572662306, + "id": "6e35c386-395c-4071-9fb5-54d6b130115f", + "label": "P0413_B84_F04_009_07_07.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/6e35c386-395c-4071-9fb5-54d6b130115f", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1073741824, + "id": "78dc79de-cb76-44b5-8010-d993056fe9d2", + "label": "P0413_B84_F04_009_08_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/78dc79de-cb76-44b5-8010-d993056fe9d2", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "797e081e-e798-48ee-bbc1-0ca6c619de97", + "label": "P0413_B84_F04_009_06_07.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/797e081e-e798-48ee-bbc1-0ca6c619de97", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 214748365, + "id": "820aa4ff-4c7e-49ba-8824-738a01106f14", + "label": "P0413_B84_F04_009_07_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/820aa4ff-4c7e-49ba-8824-738a01106f14", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1216907400, + "id": "82b1761f-d709-454e-8fb0-2f0ad661cb7f", + "label": "P0413_B84_F04_009_08_08.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/82b1761f-d709-454e-8fb0-2f0ad661cb7f", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1574821341, + "id": "8325efc6-8e3f-4b98-b979-99ceb05aa48f", + "label": "P0413_B84_F04_009_10_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8325efc6-8e3f-4b98-b979-99ceb05aa48f", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 2004318071, + "id": "87b8da22-a06a-45c8-ad05-5eeb5732f5be", + "label": "P0413_B84_F04_009_10_07.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/87b8da22-a06a-45c8-ad05-5eeb5732f5be", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1646404130, + "id": "88987e7a-3277-4ffd-8fce-bdbb61eccfdb", + "label": "P0413_B84_F04_009_02_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/88987e7a-3277-4ffd-8fce-bdbb61eccfdb", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1789569706, + "id": "893523db-d478-4501-9651-149c90e943c5", + "label": "P0413_B84_F04_009_10_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/893523db-d478-4501-9651-149c90e943c5", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1646404130, + "id": "8b5362e6-cfa9-4c4d-80ee-82978adf6977", + "label": "P0413_B84_F04_009_10_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8b5362e6-cfa9-4c4d-80ee-82978adf6977", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -2004318071, + "id": "8ccde0c9-9749-45a6-b087-fe7369a2ae73", + "label": "P0413_B84_F04_009_02_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8ccde0c9-9749-45a6-b087-fe7369a2ae73", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 286331153, + "id": "8f401490-5e19-4209-b18e-e701937f3acf", + "label": "P0413_B84_F04_009_07_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8f401490-5e19-4209-b18e-e701937f3acf", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1431655765, + "id": "95353321-bc0d-4ea1-b2b9-49f6e4ef6314", + "label": "P0413_B84_F04_009_02_09.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/95353321-bc0d-4ea1-b2b9-49f6e4ef6314", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 2138535799, + "id": "96024b01-28de-4ecd-9f7e-2dbb8dcb7dd4", + "label": "P0413_B84_F04_009_11_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96024b01-28de-4ecd-9f7e-2dbb8dcb7dd4", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 71582788, + "id": "96259982-ac72-4ca8-bf54-cb2f5693965c", + "label": "P0413_B84_F04_009_06_08.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96259982-ac72-4ca8-bf54-cb2f5693965c", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1503238553, + "id": "96ba2a1e-ee22-48ee-bb3e-cc8ed7e76486", + "label": "P0413_B84_F04_009_02_08.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96ba2a1e-ee22-48ee-bb3e-cc8ed7e76486", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1145324612, + "id": "96d4f624-e268-49ee-bf43-e8ddd4ff617f", + "label": "P0413_B84_F04_009_03_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96d4f624-e268-49ee-bf43-e8ddd4ff617f", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1288490188, + "id": "9ae9ad83-38b7-4454-94ea-6d1d8a10bea4", + "label": "P0413_B84_F04_009_09_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9ae9ad83-38b7-4454-94ea-6d1d8a10bea4", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 2075900859, + "id": "9b0f27f5-e564-4fcb-b8ef-710cca78fc6d", + "label": "P0413_B84_F04_009_11_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9b0f27f5-e564-4fcb-b8ef-710cca78fc6d", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -572662306, + "id": "9bd7f275-08f7-40c0-8dbb-9f62ed7a4534", + "label": "P0413_B84_F04_009_05_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9bd7f275-08f7-40c0-8dbb-9f62ed7a4534", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -286331153, + "id": "9c3153bf-9d49-47f8-97ad-d4a69856e20b", + "label": "P0413_B84_F04_009_06_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9c3153bf-9d49-47f8-97ad-d4a69856e20b", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 501079518, + "id": "9ee3fe77-87e4-4ce5-86e8-a167f092e971", + "label": "P0413_B84_F04_009_07_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9ee3fe77-87e4-4ce5-86e8-a167f092e971", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1717986918, + "id": "9f1191b2-3c34-44c0-b33c-621a199f943b", + "label": "P0413_B84_F04_009_02_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9f1191b2-3c34-44c0-b33c-621a199f943b", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1360072977, + "id": "9f984b13-0746-44a7-9e61-f8b5f8f8a5e8", + "label": "P0413_B84_F04_009_02_10.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9f984b13-0746-44a7-9e61-f8b5f8f8a5e8", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 787410671, + "id": "a3517a51-b000-4b68-acb7-bb27eade4f61", + "label": "P0413_B84_F04_009_08_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a3517a51-b000-4b68-acb7-bb27eade4f61", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1002159036, + "id": "a9676c51-43bb-4cbc-948f-3274d3d4ed5d", + "label": "P0413_B84_F04_009_04_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a9676c51-43bb-4cbc-948f-3274d3d4ed5d", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1932735283, + "id": "a9c8ef21-4482-4e12-961a-f2a41a0bb387", + "label": "P0413_B84_F04_009_02_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a9c8ef21-4482-4e12-961a-f2a41a0bb387", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -2075900859, + "id": "c6efe600-bc46-4b79-ab07-2a54014adc89", + "label": "P0413_B84_F04_009_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1574821342, + "id": "cc9e154e-bd38-4420-9a38-e17ccdcdce63", + "label": "P0413_B84_F04_009_02_07.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/cc9e154e-bd38-4420-9a38-e17ccdcdce63", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -930576247, + "id": "cd7f8d79-1f4d-4f43-b1de-0ef51a047ebd", + "label": "P0413_B84_F04_009_04_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/cd7f8d79-1f4d-4f43-b1de-0ef51a047ebd", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1503238553, + "id": "d2cc2196-c0d4-42a9-8582-c106ff5dd199", + "label": "P0413_B84_F04_009_09_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/d2cc2196-c0d4-42a9-8582-c106ff5dd199", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1073741824, + "id": "e0c8628f-40b9-4cf1-bb60-df4a026e4e67", + "label": "P0413_B84_F04_009_03_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e0c8628f-40b9-4cf1-bb60-df4a026e4e67", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -214748365, + "id": "e2af7752-dc31-4d00-ac89-02f0aeae7977", + "label": "P0413_B84_F04_009_06_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e2af7752-dc31-4d00-ac89-02f0aeae7977", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1145324612, + "id": "e8ce307a-7f3b-473d-892b-c53f87f28dc8", + "label": "P0413_B84_F04_009_08_07.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e8ce307a-7f3b-473d-892b-c53f87f28dc8", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1002159035, + "id": "ed3290ea-36fe-48d6-a22b-b2919956ea4b", + "label": "P0413_B84_F04_009_08_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ed3290ea-36fe-48d6-a22b-b2919956ea4b", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1932735283, + "id": "f1090fcb-2d66-4aa7-8d78-86a0d659628b", + "label": "P0413_B84_F04_009_10_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f1090fcb-2d66-4aa7-8d78-86a0d659628b", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -858993459, + "id": "f2e3be07-1e29-484b-8fae-b3075c7dbf05", + "label": "P0413_B84_F04_009_05_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f2e3be07-1e29-484b-8fae-b3075c7dbf05", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -715827883, + "id": "fb2e193d-c625-4bea-8aaa-67696187d28e", + "label": "P0413_B84_F04_009_05_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fb2e193d-c625-4bea-8aaa-67696187d28e", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 644245094, + "id": "fc9ff012-d5e2-49f6-aa8e-a713dee6ecb4", + "label": "P0413_B84_F04_009_07_08.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fc9ff012-d5e2-49f6-aa8e-a713dee6ecb4", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1861152494, + "id": "fee7fb28-57b5-4e39-ac24-ab1e6a5c8a9d", + "label": "P0413_B84_F04_009_10_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fee7fb28-57b5-4e39-ac24-ab1e6a5c8a9d", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1789569706, + "id": "b098b7fc-f922-404d-8361-3b173c23fed5", + "label": "P0413_B84_F04_009_02_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b098b7fc-f922-404d-8361-3b173c23fed5", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -429496730, + "id": "03abcb63-8886-4e06-bda9-2d0316537405", + "label": "P0413_B84_F04_009_06_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/03abcb63-8886-4e06-bda9-2d0316537405", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -143165577, + "id": "049050a9-e77d-4782-b97d-c6df3d7e0e45", + "label": "P0413_B84_F04_009_06_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/049050a9-e77d-4782-b97d-c6df3d7e0e45", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 715827882, + "id": "0b514433-ce5e-4bf8-8e9c-f74f9bc666c7", + "label": "P0413_B84_F04_009_08_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0b514433-ce5e-4bf8-8e9c-f74f9bc666c7", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -644245094, + "id": "0e691e0b-85e0-4c64-b47f-8dc8aa65249c", + "label": "P0413_B84_F04_009_05_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0e691e0b-85e0-4c64-b47f-8dc8aa65249c", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1288490189, + "id": "0eadcacd-43cc-4789-a751-549e6d4161dd", + "label": "P0413_B84_F04_009_03_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0eadcacd-43cc-4789-a751-549e6d4161dd", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 858993459, + "id": "12e1a575-7578-4215-a786-a6716f08117c", + "label": "P0413_B84_F04_009_08_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/12e1a575-7578-4215-a786-a6716f08117c", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1717986918, + "id": "179ba4ce-2b21-4842-bc25-b96a4492be50", + "label": "P0413_B84_F04_009_10_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/179ba4ce-2b21-4842-bc25-b96a4492be50", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -357913941, + "id": "198c0b2a-1fb6-4488-b448-96c065aad25e", + "label": "P0413_B84_F04_009_06_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/198c0b2a-1fb6-4488-b448-96c065aad25e", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 930576247, + "id": "1fd583bb-f1d5-481b-ab29-97b21425560b", + "label": "P0413_B84_F04_009_08_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/1fd583bb-f1d5-481b-ab29-97b21425560b", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 1431655765, + "id": "20739142-0e37-420e-81b4-e6d9fcbdf6d6", + "label": "P0413_B84_F04_009_09_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/20739142-0e37-420e-81b4-e6d9fcbdf6d6", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 2111692253, + "id": "27b1e8ad-3453-40ce-bc95-4513a74dabfe", + "label": "P0413_B84_F04_009_11_02.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/27b1e8ad-3453-40ce-bc95-4513a74dabfe", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 143165576, + "id": "2b41a266-cd5c-4b9f-ba8c-c9731077ab84", + "label": "P0413_B84_F04_009_07_01.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2b41a266-cd5c-4b9f-ba8c-c9731077ab84", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -501079518, + "id": "2df2e469-2a45-44d1-9d74-5b9324df8b14", + "label": "P0413_B84_F04_009_05_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2df2e469-2a45-44d1-9d74-5b9324df8b14", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 2129587950, + "id": "2ec83ff3-56b4-4105-ac36-8da8b4dd23a6", + "label": "P0413_B84_F04_009_11_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2ec83ff3-56b4-4105-ac36-8da8b4dd23a6", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 429496729, + "id": "46cd43fe-5e01-4bec-9539-c38367a1e906", + "label": "P0413_B84_F04_009_07_05.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/46cd43fe-5e01-4bec-9539-c38367a1e906", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -1861152495, + "id": "4dd19cf4-8224-490d-8a67-3e72a183cb26", + "label": "P0413_B84_F04_009_02_03.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/4dd19cf4-8224-490d-8a67-3e72a183cb26", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 357913941, + "id": "56c1d785-7144-496d-8b07-c6366161fbe6", + "label": "P0413_B84_F04_009_07_04.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/56c1d785-7144-496d-8b07-c6366161fbe6", + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": -71582788, + "id": "59facd76-ac09-4eba-a558-0580ed1500f5", + "label": "P0413_B84_F04_009_06_06.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/59facd76-ac09-4eba-a558-0580ed1500f5", + "poster_offset": null + } + ], + "status": "Not Started", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": "Vernon McKay", + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/ee592440-4401-445b-8b8d-2991b2817e57", + "ark": "ark:/81985/n2pv6dq8q", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": { + "fileSetId": "c6efe600-bc46-4b79-ab07-2a54014adc89", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "Level 1" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "ae1f46c8-466c-49d8-8cf9-af586969c4ce", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.613518", + "rights_statement": null, + "id": "ae1f46c8-466c-49d8-8cf9-af586969c4ce", + "create_date": "2022-06-27T21:14:38.058837Z", + "identifier": [], + "box_number": [], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e/full/!300,300/0/default.jpg", + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ae/1f/46/c8/-4/66/c-/49/d8/-8/cf/9-/af/58/69/69/c4/ce-manifest.json", + "work_type": "Image", + "collection": {}, + "published": false, + "modified_date": "2022-06-27T21:14:38.211263Z", + "accession_number": "6-27_test_work_920", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "815972ba-ad41-4275-9897-77563412e82e", + "label": "label", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e", + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/ae1f46c8-466c-49d8-8cf9-af586969c4ce", + "ark": "ark:/99999/fk49k5vr1p", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": { + "fileSetId": "815972ba-ad41-4275-9897-77563412e82e", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "d5c73843-550d-47aa-8abb-cd8013ed32af", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.694440", + "rights_statement": null, + "id": "d5c73843-550d-47aa-8abb-cd8013ed32af", + "create_date": "2022-06-27T21:13:53.700541Z", + "identifier": [], + "box_number": [], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e/full/!300,300/0/default.jpg", + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/d5/c7/38/43/-5/50/d-/47/aa/-8/ab/b-/cd/80/13/ed/32/af-manifest.json", + "work_type": "Image", + "collection": {}, + "published": false, + "modified_date": "2022-06-27T21:13:53.861314Z", + "accession_number": "6-27_test_work_535", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Preservation", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "8de19e63-a166-40cf-9196-dc605191d7dd", + "label": "label", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "01c3b28f-df15-4055-a947-b0f192e94d8e", + "label": "label", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e", + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/d5c73843-550d-47aa-8abb-cd8013ed32af", + "ark": "ark:/99999/fk4892qp5c", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": { + "fileSetId": "01c3b28f-df15-4055-a947-b0f192e94d8e", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "ed9c7967-62ab-416c-a3af-492ce865549f", + "_score": 1.0, + "_source": { + "keywords": [], + "caption": [], + "source": [], + "terms_of_use": null, + "library_unit": "", + "indexed_at": "2022-07-28T15:45:31.706916", + "rights_statement": null, + "id": "ed9c7967-62ab-416c-a3af-492ce865549f", + "create_date": "2022-06-27T21:15:38.960884Z", + "identifier": [], + "box_number": [], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b/full/!300,300/0/default.jpg", + "visibility": "Private", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ed/9c/79/67/-6/2a/b-/41/6c/-a/3a/f-/49/2c/e8/65/54/9f-manifest.json", + "work_type": "Image", + "collection": {}, + "published": false, + "modified_date": "2022-06-27T21:15:39.123581Z", + "accession_number": "6-27_test_work_827", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "e17dd686-0749-4466-8983-27b5b62ff27b", + "label": "label", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b", + "poster_offset": null + }, + { + "role": "Preservation", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "0a5177bb-e75b-4492-b2af-342fd5022774", + "label": "label", + "representative_image_url": null, + "poster_offset": null + } + ], + "status": "", + "folder_numbers": [], + "box_name": [], + "catalog_key": [], + "title": null, + "legacy_identifier": [], + "provenance": [], + "api_link": "[PLACEHOLDER]/ed9c7967-62ab-416c-a3af-492ce865549f", + "ark": "ark:/99999/fk4st93v3f", + "alternate_title": [], + "related_material": [], + "folder_names": [], + "abstract": [], + "representative_file_set": { + "fileSetId": "e17dd686-0749-4466-8983-27b5b62ff27b", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "" + } + }, + { + "_index": "dc-v2-work-1658438320350940", + "_type": "_doc", + "_id": "8d710f86-ec54-43cf-9ec4-eb71b1c90c5d", + "_score": 1.0, + "_source": { + "keywords": [], + "subject": [ + { + "role": "Topical", + "id": "http://id.loc.gov/authorities/names/n87130885", + "label": "Fariña, Mimi", + "label_with_role": "Fariña, Mimi (Topical)" + } + ], + "caption": [], + "source": [], + "terms_of_use": "The images on this web site are from material in the collections of the Charles Deering McCormick Library of Special Collections of Northwestern University Libraries, and are provided for use by its students, faculty and staff, and by other researchers visiting this site, for research consultation and scholarly purposes only. Further distribution and/or any commercial use of the images from this site is not permitted.", + "library_unit": "Charles Deering McCormick Library of Special Collections", + "indexed_at": "2022-07-28T15:45:31.784735", + "rights_statement": { + "id": "http://rightsstatements.org/vocab/InC/1.0/", + "label": "In Copyright" + }, + "id": "8d710f86-ec54-43cf-9ec4-eb71b1c90c5d", + "create_date": "2021-03-16T05:58:30.479386Z", + "identifier": ["MS 63"], + "box_number": ["3"], + "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44/full/!300,300/0/default.jpg", + "visibility": "Public", + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/8d/71/0f/86/-e/c5/4-/43/cf/-9/ec/4-/eb/71/b1/c9/0c/5d-manifest.json", + "work_type": "Image", + "collection": { + "id": "18ec4c6b-192a-4ab8-9903-ea0f393c35f7", + "title": "Berkeley Folk Music Festival" + }, + "published": true, + "modified_date": "2022-05-02T21:10:34.982567Z", + "accession_number": "BFMF_B03_F19_005_001n", + "license": null, + "physical_description_size": [], + "publisher": [], + "file_sets": [ + { + "role": "Preservation", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "9a1cd92a-fafc-4081-8213-cf6a8783a864", + "label": "Mimi Farina. Digital image scanned from black and white negative.", + "representative_image_url": null, + "poster_offset": null + }, + { + "role": "Access", + "streaming_url": null, + "mime_type": "image/tiff", + "rank": 0, + "id": "ac987e48-b6a5-4d34-b5e0-1d2006996a44", + "label": "BFMF_B03_F19_005_001n_am.tif", + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44", + "poster_offset": null + } + ], + "status": "Done", + "folder_numbers": ["19"], + "box_name": [], + "catalog_key": [], + "title": "Mimi Farina", + "legacy_identifier": [], + "provenance": [], + "genre": [ + { + "id": "http://vocab.getty.edu/aat/300128343", + "label": "black-and-white negatives" + } + ], + "api_link": "[PLACEHOLDER]/8d710f86-ec54-43cf-9ec4-eb71b1c90c5d", + "ark": "ark:/81985/n2h41k11r", + "alternate_title": [], + "related_material": [], + "folder_names": ["Farina, Mimi"], + "abstract": [], + "representative_file_set": { + "fileSetId": "ac987e48-b6a5-4d34-b5e0-1d2006996a44", + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44" + }, + "api_model": "Work", + "physical_description_material": [], + "series": [ + "Berkeley Folk Music Festival Archive -- 1. Artists' Photo Archive" + ], + "rights_holder": [], + "scope_and_contents": [], + "table_of_contents": [], + "preservation_level": "Level 1" + } + } + ] + } +} diff --git a/tests/unit/api/opensearch.test.js b/tests/unit/api/opensearch.test.js index be638c57..83d676a3 100644 --- a/tests/unit/api/opensearch.test.js +++ b/tests/unit/api/opensearch.test.js @@ -62,3 +62,41 @@ describe("getCollection()", function () { expect(body._source.api_model).to.eq("Collection"); }); }); + +describe("search()", function () { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + it("performs searches", async function () { + mock + .post("/dc-v2-work/_search", "{ query: { match_all: {} } }") + .reply(200, helpers.testFixture("mocks/search.json")); + + const result = await opensearch.search( + "dc-v2-work", + "{ query: { match_all: {} } }" + ); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(200); + expect(body.hits.hits.length).to.eq(10); + expect(body.hits.total.value).to.eq(4199); + }); + + it("can search multiple targets", async function () { + mock + .post( + "/dc-v2-work,dc-v2-collection/_search", + "{ query: { match_all: {} } }" + ) + .reply(200, helpers.testFixture("mocks/search-multiple-targets.json")); + + const result = await opensearch.search( + "dc-v2-work,dc-v2-collection", + "{ query: { match_all: {} } }" + ); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(200); + expect(body.hits.hits.length).to.eq(10); + expect(body.hits.total.value).to.eq(4331); + }); +}); From 3f067db260297baa730b8130b428a438cf53c529 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Tue, 9 Aug 2022 09:12:58 -0500 Subject: [PATCH 03/61] Add a test example for calling getWork() on a missing document returning a 404 --- package-lock.json | 1607 ++++------------- package.json | 2 +- tests/fixtures/mocks/missing-work-1234.json | 9 +- .../fixtures/mocks/unpublished-work-1234.json | 13 + tests/unit/api/opensearch.test.js | 13 +- 5 files changed, 382 insertions(+), 1262 deletions(-) create mode 100644 tests/fixtures/mocks/unpublished-work-1234.json diff --git a/package-lock.json b/package-lock.json index f19082dd..87682012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dc-api", - "version": "0.1.0", + "version": "2.0.0pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dc-api", - "version": "0.1.0", + "version": "2.0.0pre", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "^2.0.1", @@ -26,9 +26,8 @@ }, "node_modules/@ampproject/remapping": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -39,21 +38,18 @@ }, "node_modules/@aws-crypto/ie11-detection": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", - "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-crypto/sha256-browser": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", - "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/ie11-detection": "^2.0.0", "@aws-crypto/sha256-js": "^2.0.1", @@ -67,8 +63,7 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@aws-crypto/sha256-js": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", - "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^2.0.1", "@aws-sdk/types": "^3.1.0", @@ -77,13 +72,11 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-crypto/sha256-js": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^2.0.0", "@aws-sdk/types": "^3.1.0", @@ -92,26 +85,22 @@ }, "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", - "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^1.11.1" } }, "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-crypto/util": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", - "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.1.0", "@aws-sdk/util-utf8-browser": "^3.0.0", @@ -120,13 +109,11 @@ }, "node_modules/@aws-crypto/util/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-sdk/abort-controller": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.127.0.tgz", - "integrity": "sha512-G77FLYcl9egUoD3ZmR6TX94NMqBMeT53hBGrEE3uVUJV1CwfGKfaF007mPpRZnIB3avnJBQGEK6MrwlCfv2qAw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -137,8 +124,7 @@ }, "node_modules/@aws-sdk/client-sso": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.142.0.tgz", - "integrity": "sha512-Pewcpxq+wqcbB3t3s6KImBUUf+qqBNqMfd2wgQ3PdpYBjlNzrWYLHAnIT1vhIFjOGJXDi/qwF8FX7qbWNUB7Lg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", @@ -178,8 +164,7 @@ }, "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/ie11-detection": "^2.0.0", "@aws-crypto/sha256-js": "^2.0.0", @@ -193,13 +178,11 @@ }, "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "license": "0BSD" }, "node_modules/@aws-sdk/config-resolver": { "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.130.0.tgz", - "integrity": "sha512-7dkCHHI9kRcHW6YNr9/2Ub6XkvU9Fu6H/BnlKbaKlDR8jq7QpaFhPhctOVi5D/NDpxJgALifexFne0dvo3piTw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/signature-v4": "3.130.0", "@aws-sdk/types": "3.127.0", @@ -213,8 +196,7 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.127.0.tgz", - "integrity": "sha512-Ig7XhUikRBlnRTYT5JBGzWfYZp68X5vkFVIFCmsHHt/qVy0Nz9raZpmDHicdS1u67yxDkWgCPn/bNevWnM0GFg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -226,8 +208,7 @@ }, "node_modules/@aws-sdk/credential-provider-imds": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.127.0.tgz", - "integrity": "sha512-I6KlIBBzmJn/U1KikiC50PK3SspT9G5lkVLBaW5a6YfOcijqVTXfAN3kYzqhfeS0j4IgfJEwKVsjsZfmprJO5A==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/property-provider": "3.127.0", @@ -241,8 +222,7 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.142.0.tgz", - "integrity": "sha512-joMJTxUTNmxURnVmmd7XhtwOwijMjh7z09V8o2EHQMk+ak+rhaRgqb2kTA2nO0J3SRxdO5z5SKkyQgW0d1fY9g==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -259,8 +239,7 @@ }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.142.0.tgz", - "integrity": "sha512-JkhCKNkEhCS2vgD/qg5hJPatupNLObqts9FXiDia5CF6w8YcHLH+mWSvhUMCUGkunAOvFHDkQL1uPXfoQuJvPg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -279,8 +258,7 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.127.0.tgz", - "integrity": "sha512-6v0m2lqkO9J5fNlTl+HjriQNIdfg8mjVST544+5y9EnC/FVmTnIz64vfHveWdNkP/fehFx7wTimNENtoSqCn3A==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -293,8 +271,7 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.142.0.tgz", - "integrity": "sha512-jJvp/A5xrikaeL0DhjhQTvUc0+eF0hN5Nbo+nxpnUOiOOkyqs329g65NI1TmLp/OzDcqQ/8p5vj2+7ufTGEOXQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-sso": "3.142.0", "@aws-sdk/property-provider": "3.127.0", @@ -308,8 +285,7 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.127.0.tgz", - "integrity": "sha512-85ahDZnLYB3dqkW+cQ0bWt+NVqOoxomTrJoq3IC2q6muebeFrJ0pyf0JEW/RNRzBiUvvsZujzGdWifzWyQKfVg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -321,8 +297,7 @@ }, "node_modules/@aws-sdk/fetch-http-handler": { "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.131.0.tgz", - "integrity": "sha512-eNxmPZQX2IUeBGWHNC7eNTekWn9VIPLYEMKJbKYUBJryxuTJ7TtLeyEK5oakUjMwP1AUvWT+CV7C+8L7uG1omQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/querystring-builder": "3.127.0", @@ -333,8 +308,7 @@ }, "node_modules/@aws-sdk/hash-node": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.127.0.tgz", - "integrity": "sha512-wx7DKlXdKebH4JcMsOevdsm2oDNMVm36kuMm0XWRIrFWQ/oq7OquDpEMJzWvGqWF/IfFUpb7FhAWZZpALwlcwA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "@aws-sdk/util-buffer-from": "3.55.0", @@ -346,8 +320,7 @@ }, "node_modules/@aws-sdk/invalid-dependency": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.127.0.tgz", - "integrity": "sha512-bxvmtmJ6gIRfOHvh1jAPZBH2mzppEblPjEOFo4mOzXz4U3qPIxeuukCjboMnGK9QEpV2wObWcYYld0vxoRrfiA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -355,8 +328,7 @@ }, "node_modules/@aws-sdk/is-array-buffer": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", - "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -366,8 +338,7 @@ }, "node_modules/@aws-sdk/middleware-content-length": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.127.0.tgz", - "integrity": "sha512-AFmMaIEW3Rzg0TaKB9l/RENLowd7ZEEOpm0trYw1CgUUORWW/ydCsDT7pekPlC25CPbhUmWXCSA4xPFSYOVnDw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -379,8 +350,7 @@ }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.127.0.tgz", - "integrity": "sha512-e2gTLJb5lYP9lRV7hN3rKY2l4jv8OygOoHElZJ3Z8KPZskjHelYPcQ8XbdfhSXXxC3vc/0QqN0ResFt3W3Pplg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -392,8 +362,7 @@ }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.127.0.tgz", - "integrity": "sha512-jMNLcZB/ECA7OfkNBLNeAlrLRehyfnUeNQJHW3kcxs9h1+6VxaF6wY+WKozszLI7/3OBzQrFHBQCfRZV7ykSLg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -404,8 +373,7 @@ }, "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.127.0.tgz", - "integrity": "sha512-tB6WX+Z1kUKTnn5h38XFrTCzoqPKjUZLUjN4Wb27/cbeSiTSKGAZcCXHOJm36Ukorl5arlybQTqGe689EU00Hw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -417,8 +385,7 @@ }, "node_modules/@aws-sdk/middleware-retry": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.127.0.tgz", - "integrity": "sha512-ZSvg/AyGUacWnf3i8ZbyImtiCH+NyafF8uV7bITP7JkwPrG+VdNocJZOr88GRM0c1A0jfkOf7+oq+fInPwwiNA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/service-error-classification": "3.127.0", @@ -433,8 +400,7 @@ }, "node_modules/@aws-sdk/middleware-serde": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.127.0.tgz", - "integrity": "sha512-xmWMYV/t9M+b9yHjqaD1noDNJJViI2QwOH7TQZ9VbbrvdVtDrFuS9Sf9He80TBCJqeHShwQN9783W1I3Pu/8kw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -445,8 +411,7 @@ }, "node_modules/@aws-sdk/middleware-stack": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.127.0.tgz", - "integrity": "sha512-S1IoUE5o1vCmjsF5nIE8zlItNOM1UE+lhmZeigF7knXJ9+a6ewMB6POAj/s4eoi0wcn0eSnAGsqJCWMSUjOPLA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -456,8 +421,7 @@ }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.127.0.tgz", - "integrity": "sha512-CHxgswoOzdkOEoIq7Oyob3Sx/4FYUv6BhUesAX7MNshaDDsTQPbSWjw5bqZDiL/gO+X/34fvqCVVpVD2GvxW/g==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -469,8 +433,7 @@ }, "node_modules/@aws-sdk/node-config-provider": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.127.0.tgz", - "integrity": "sha512-bAHkASMhLZHT1yv2TX6OJGFV9Lc3t1gKfTMEKdXM2O2YhGfSx9A/qLeJm79oDfnILWQtSS2NicxlRDI2lYGf4g==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -483,8 +446,7 @@ }, "node_modules/@aws-sdk/node-http-handler": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.127.0.tgz", - "integrity": "sha512-pyMKvheK8eDwWLgYIRsWy8wiyhsbYYcqkZQs3Eh6upI4E8iCY7eMmhWvHYCibvsO+UjsOwa4cAMOfwnv/Z9s8A==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/abort-controller": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", @@ -498,8 +460,7 @@ }, "node_modules/@aws-sdk/property-provider": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.127.0.tgz", - "integrity": "sha512-JxenxlTEkWfLrtJqIjaXaJzAVQbbscoCb5bNjmdud07ESLVfWRKJx2nAJdecHKYp2M5NQyqBuFhQ1ELSFYQKCA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -510,8 +471,7 @@ }, "node_modules/@aws-sdk/protocol-http": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.127.0.tgz", - "integrity": "sha512-UG83PVuKX40wilG2uRU0Fvz4OY8Bt+bSPOG776DFjwIXYzK7BwpJm9H2XI2HLhS5WxrJHhwrLBRgW6UiykMnFw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -522,8 +482,7 @@ }, "node_modules/@aws-sdk/querystring-builder": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.127.0.tgz", - "integrity": "sha512-tsoyp4lLPsASPDYWsezGAHD8VJsZbjUNATNAzTCFdH6p+4SKBK83Q5kfXCzxt13M+l3oKbxxIWLvS0kVQFyltQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "@aws-sdk/util-uri-escape": "3.55.0", @@ -535,8 +494,7 @@ }, "node_modules/@aws-sdk/querystring-parser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.127.0.tgz", - "integrity": "sha512-Vn/Dv+PqUSepp/DzLqq0LJJD8HdPefJCnLbO5WcHCARHSGlyGlZUFEM45k/oEHpTvgMXj/ORaP3A+tLwLu0AmA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -547,16 +505,14 @@ }, "node_modules/@aws-sdk/service-error-classification": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.127.0.tgz", - "integrity": "sha512-wjZY9rnlA8SPrICUumTYicEKtK4/yKB62iadUk66hxe8MrH8JhuHH2NqIad0Pt/bK/YtNVhd3yb4pRapOeY5qQ==", + "license": "Apache-2.0", "engines": { "node": ">= 12.0.0" } }, "node_modules/@aws-sdk/shared-ini-file-loader": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.127.0.tgz", - "integrity": "sha512-S3Nn4KRTqoJsB/TbRZSWBBUrkckNMR0Juqz7bOB+wupVvddKP6IcpspSC/GX9zgJjVMV8iGisZ6AUsYsC5r+cA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -566,8 +522,7 @@ }, "node_modules/@aws-sdk/signature-v4": { "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.130.0.tgz", - "integrity": "sha512-g5G1a1NHL2uOoFfC2zQdZcj+wbjgBQPkx6xGdtqNKf9v2kS0n6ap5JUGEaqWE02lUlmWHsoMsS73hXtzwXaBRQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/is-array-buffer": "3.55.0", "@aws-sdk/types": "3.127.0", @@ -582,8 +537,7 @@ }, "node_modules/@aws-sdk/smithy-client": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", - "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/middleware-stack": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -595,16 +549,14 @@ }, "node_modules/@aws-sdk/types": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.127.0.tgz", - "integrity": "sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og==", + "license": "Apache-2.0", "engines": { "node": ">= 12.0.0" } }, "node_modules/@aws-sdk/url-parser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.127.0.tgz", - "integrity": "sha512-njZ7zn41JHRpNfr3BCesVXCLZE0zcWSfEdtRV0ICw0cU1FgYcKELSuY9+gLUB4ci6uc7gq7mPE8+w30FcM4QeA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/querystring-parser": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -613,16 +565,14 @@ }, "node_modules/@aws-sdk/util-base64-browser": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", - "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" } }, "node_modules/@aws-sdk/util-base64-node": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", - "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/util-buffer-from": "3.55.0", "tslib": "^2.3.1" @@ -633,16 +583,14 @@ }, "node_modules/@aws-sdk/util-body-length-browser": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", - "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" } }, "node_modules/@aws-sdk/util-body-length-node": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", - "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -652,8 +600,7 @@ }, "node_modules/@aws-sdk/util-buffer-from": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", - "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/is-array-buffer": "3.55.0", "tslib": "^2.3.1" @@ -664,8 +611,7 @@ }, "node_modules/@aws-sdk/util-config-provider": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", - "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -675,8 +621,7 @@ }, "node_modules/@aws-sdk/util-defaults-mode-browser": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", - "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -689,8 +634,7 @@ }, "node_modules/@aws-sdk/util-defaults-mode-node": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", - "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/config-resolver": "3.130.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -705,8 +649,7 @@ }, "node_modules/@aws-sdk/util-hex-encoding": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", - "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -716,8 +659,7 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", - "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -727,8 +669,7 @@ }, "node_modules/@aws-sdk/util-middleware": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.127.0.tgz", - "integrity": "sha512-EwAPPed9TNqh+Wov2VStLn2NuJ/Wyt7IkZCbCsBuSNp3BFZ1V4gfwTjqtKCtB2LQgQ48MTgWgNCvrH0zjCSPGg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -738,8 +679,7 @@ }, "node_modules/@aws-sdk/util-uri-escape": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", - "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" }, @@ -749,8 +689,7 @@ }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.127.0.tgz", - "integrity": "sha512-uO2oHmJswuYKJS+GiMdYI8izhpC9M7/jFFvnAmLlTEVwpEi1VX9KePAOF+u5AaBC2kzITo/7dg141XfRHZloIQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.127.0", "bowser": "^2.11.0", @@ -759,8 +698,7 @@ }, "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.127.0.tgz", - "integrity": "sha512-3P/M4ZDD2qMeeoCk7TE/Mw7cG5IjB87F6BP8nI8/oHuaz7j6fsI7D49SNpyjl8JApRynZ122Ad6hwQwRj3isYw==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -780,16 +718,14 @@ }, "node_modules/@aws-sdk/util-utf8-browser": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", - "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.1" } }, "node_modules/@aws-sdk/util-utf8-node": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", - "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/util-buffer-from": "3.55.0", "tslib": "^2.3.1" @@ -800,9 +736,8 @@ }, "node_modules/@babel/code-frame": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -812,18 +747,16 @@ }, "node_modules/@babel/compat-data": { "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -851,9 +784,8 @@ }, "node_modules/@babel/generator": { "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.10", "@jridgewell/gen-mapping": "^0.3.2", @@ -865,9 +797,8 @@ }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -879,9 +810,8 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.18.8", "@babel/helper-validator-option": "^7.18.6", @@ -897,18 +827,16 @@ }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.18.6", "@babel/types": "^7.18.9" @@ -919,9 +847,8 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -931,9 +858,8 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -943,9 +869,8 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", @@ -962,9 +887,8 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -974,9 +898,8 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -986,36 +909,32 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.18.6", "@babel/traverse": "^7.18.9", @@ -1027,9 +946,8 @@ }, "node_modules/@babel/highlight": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -1041,9 +959,8 @@ }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -1053,9 +970,8 @@ }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1067,42 +983,37 @@ }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -1112,9 +1023,8 @@ }, "node_modules/@babel/parser": { "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -1124,9 +1034,8 @@ }, "node_modules/@babel/template": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.18.10", @@ -1138,9 +1047,8 @@ }, "node_modules/@babel/traverse": { "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.18.10", @@ -1159,9 +1067,8 @@ }, "node_modules/@babel/types": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", @@ -1173,9 +1080,8 @@ }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1189,27 +1095,24 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1220,9 +1123,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1233,9 +1135,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1245,9 +1146,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1260,9 +1160,8 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1272,18 +1171,16 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1294,33 +1191,29 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1328,15 +1221,13 @@ }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -1347,27 +1238,24 @@ }, "node_modules/ansi-colors": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -1380,9 +1268,8 @@ }, "node_modules/anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1393,9 +1280,8 @@ }, "node_modules/append-transform": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, + "license": "MIT", "dependencies": { "default-require-extensions": "^3.0.0" }, @@ -1405,34 +1291,29 @@ }, "node_modules/archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "license": "MIT" }, "node_modules/axios": { "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" @@ -1440,29 +1321,25 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/bowser": { "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1470,9 +1347,8 @@ }, "node_modules/braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -1482,14 +1358,11 @@ }, "node_modules/browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserslist": { "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "funding": [ { @@ -1501,6 +1374,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001370", "electron-to-chromium": "^1.4.202", @@ -1516,9 +1390,8 @@ }, "node_modules/caching-transform": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, + "license": "MIT", "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", @@ -1531,9 +1404,8 @@ }, "node_modules/camelcase": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1543,8 +1415,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001374", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", - "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", "dev": true, "funding": [ { @@ -1555,13 +1425,13 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chai": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, + "license": "MIT", "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", @@ -1577,9 +1447,8 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1593,9 +1462,8 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1605,17 +1473,14 @@ }, "node_modules/check-error": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { @@ -1623,6 +1488,7 @@ "url": "https://paulmillr.com/funding/" } ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1641,18 +1507,16 @@ }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -1661,9 +1525,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1673,14 +1536,12 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1690,36 +1551,31 @@ }, "node_modules/commondir": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/convert-source-map": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.1" } }, "node_modules/convert-source-map/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1731,9 +1587,8 @@ }, "node_modules/debug": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -1748,15 +1603,13 @@ }, "node_modules/debug/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1766,9 +1619,8 @@ }, "node_modules/deep-eql": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, + "license": "MIT", "dependencies": { "type-detect": "^4.0.0" }, @@ -1778,9 +1630,8 @@ }, "node_modules/default-require-extensions": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, + "license": "MIT", "dependencies": { "strip-bom": "^4.0.0" }, @@ -1790,53 +1641,46 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/diff": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/electron-to-chromium": { "version": "1.4.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", - "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/es6-error": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1846,9 +1690,8 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -1859,9 +1702,8 @@ }, "node_modules/fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1871,9 +1713,8 @@ }, "node_modules/find-cache-dir": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, + "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -1888,9 +1729,8 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1904,23 +1744,21 @@ }, "node_modules/flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/follow-redirects": { "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -1932,9 +1770,8 @@ }, "node_modules/foreground-child": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" @@ -1945,8 +1782,7 @@ }, "node_modules/form-data": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -1958,8 +1794,6 @@ }, "node_modules/fromentries": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true, "funding": [ { @@ -1974,55 +1808,50 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-func-name": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/glob": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2040,9 +1869,8 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -2052,9 +1880,8 @@ }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2064,42 +1891,37 @@ }, "node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/growl": { "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.x" } }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/hasha": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, + "license": "MIT", "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" @@ -2113,42 +1935,37 @@ }, "node_modules/he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2156,15 +1973,13 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2174,27 +1989,24 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -2204,27 +2016,24 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -2234,15 +2043,13 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2252,33 +2059,29 @@ }, "node_modules/is-windows": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-hook": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "append-transform": "^2.0.0" }, @@ -2288,9 +2091,8 @@ }, "node_modules/istanbul-lib-instrument": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", @@ -2303,9 +2105,8 @@ }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, + "license": "ISC", "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", @@ -2320,9 +2121,8 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", @@ -2334,9 +2134,8 @@ }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2346,9 +2145,8 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -2360,9 +2158,8 @@ }, "node_modules/istanbul-reports": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -2373,15 +2170,13 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2391,9 +2186,8 @@ }, "node_modules/jsesc": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -2403,15 +2197,13 @@ }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/json5": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -2421,9 +2213,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -2436,21 +2227,18 @@ }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.flattendeep": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -2464,18 +2252,16 @@ }, "node_modules/loupe": { "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, + "license": "MIT", "dependencies": { "get-func-name": "^2.0.0" } }, "node_modules/make-dir": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -2488,16 +2274,14 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -2507,9 +2291,8 @@ }, "node_modules/minimatch": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2519,9 +2302,8 @@ }, "node_modules/mocha": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, + "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -2562,15 +2344,13 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2580,9 +2360,8 @@ }, "node_modules/nock": { "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", @@ -2595,9 +2374,8 @@ }, "node_modules/node-preload": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, + "license": "MIT", "dependencies": { "process-on-spawn": "^1.0.0" }, @@ -2607,24 +2385,21 @@ }, "node_modules/node-releases": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/nyc": { "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", @@ -2663,18 +2438,16 @@ }, "node_modules/nyc/node_modules/camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -2683,18 +2456,16 @@ }, "node_modules/nyc/node_modules/decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -2705,9 +2476,8 @@ }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -2717,9 +2487,8 @@ }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -2732,9 +2501,8 @@ }, "node_modules/nyc/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -2744,9 +2512,8 @@ }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2758,15 +2525,13 @@ }, "node_modules/nyc/node_modules/y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -2786,9 +2551,8 @@ }, "node_modules/nyc/node_modules/yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -2799,18 +2563,16 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2823,9 +2585,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -2838,9 +2599,8 @@ }, "node_modules/p-map": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -2850,18 +2610,16 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/package-hash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, + "license": "ISC", "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", @@ -2874,51 +2632,45 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pathval": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } }, "node_modules/picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2928,9 +2680,8 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -2940,9 +2691,8 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -2953,9 +2703,8 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -2965,9 +2714,8 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -2980,9 +2728,8 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -2992,9 +2739,8 @@ }, "node_modules/prettier": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -3007,9 +2753,8 @@ }, "node_modules/process-on-spawn": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", "dev": true, + "license": "MIT", "dependencies": { "fromentries": "^1.2.0" }, @@ -3019,27 +2764,24 @@ }, "node_modules/propagate": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -3049,9 +2791,8 @@ }, "node_modules/release-zalgo": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, + "license": "ISC", "dependencies": { "es6-error": "^4.0.1" }, @@ -3061,33 +2802,29 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -3100,8 +2837,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -3116,37 +2851,34 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/semver": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3156,33 +2888,29 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/spawn-wrap": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", @@ -3197,15 +2925,13 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3217,9 +2943,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3229,18 +2954,16 @@ }, "node_modules/strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3250,9 +2973,8 @@ }, "node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3265,9 +2987,8 @@ }, "node_modules/test-exclude": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -3279,9 +3000,8 @@ }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3291,18 +3011,16 @@ }, "node_modules/to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -3312,40 +3030,34 @@ }, "node_modules/tslib": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "license": "0BSD" }, "node_modules/type-detect": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/type-fest": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } }, "node_modules/update-browserslist-db": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "funding": [ { @@ -3357,6 +3069,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -3370,17 +3083,15 @@ }, "node_modules/uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3393,21 +3104,18 @@ }, "node_modules/which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/workerpool": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3422,15 +3130,13 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -3440,18 +3146,16 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -3467,18 +3171,16 @@ }, "node_modules/yargs-parser": { "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -3491,9 +3193,8 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3505,8 +3206,6 @@ "dependencies": { "@ampproject/remapping": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.1.0", @@ -3515,23 +3214,17 @@ }, "@aws-crypto/ie11-detection": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", - "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", "requires": { "tslib": "^1.11.1" }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } }, "@aws-crypto/sha256-browser": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", - "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", "requires": { "@aws-crypto/ie11-detection": "^2.0.0", "@aws-crypto/sha256-js": "^2.0.1", @@ -3545,8 +3238,6 @@ "dependencies": { "@aws-crypto/sha256-js": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", - "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", "requires": { "@aws-crypto/util": "^2.0.1", "@aws-sdk/types": "^3.1.0", @@ -3554,16 +3245,12 @@ } }, "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } }, "@aws-crypto/sha256-js": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", "requires": { "@aws-crypto/util": "^2.0.0", "@aws-sdk/types": "^3.1.0", @@ -3571,31 +3258,23 @@ }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } }, "@aws-crypto/supports-web-crypto": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", - "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", "requires": { "tslib": "^1.11.1" }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } }, "@aws-crypto/util": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", - "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", "requires": { "@aws-sdk/types": "^3.1.0", "@aws-sdk/util-utf8-browser": "^3.0.0", @@ -3603,16 +3282,12 @@ }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } }, "@aws-sdk/abort-controller": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.127.0.tgz", - "integrity": "sha512-G77FLYcl9egUoD3ZmR6TX94NMqBMeT53hBGrEE3uVUJV1CwfGKfaF007mPpRZnIB3avnJBQGEK6MrwlCfv2qAw==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3620,8 +3295,6 @@ }, "@aws-sdk/client-sso": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.142.0.tgz", - "integrity": "sha512-Pewcpxq+wqcbB3t3s6KImBUUf+qqBNqMfd2wgQ3PdpYBjlNzrWYLHAnIT1vhIFjOGJXDi/qwF8FX7qbWNUB7Lg==", "requires": { "@aws-crypto/sha256-browser": "2.0.0", "@aws-crypto/sha256-js": "2.0.0", @@ -3658,8 +3331,6 @@ "dependencies": { "@aws-crypto/sha256-browser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", "requires": { "@aws-crypto/ie11-detection": "^2.0.0", "@aws-crypto/sha256-js": "^2.0.0", @@ -3672,9 +3343,7 @@ }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "1.14.1" } } } @@ -3682,8 +3351,6 @@ }, "@aws-sdk/config-resolver": { "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.130.0.tgz", - "integrity": "sha512-7dkCHHI9kRcHW6YNr9/2Ub6XkvU9Fu6H/BnlKbaKlDR8jq7QpaFhPhctOVi5D/NDpxJgALifexFne0dvo3piTw==", "requires": { "@aws-sdk/signature-v4": "3.130.0", "@aws-sdk/types": "3.127.0", @@ -3694,8 +3361,6 @@ }, "@aws-sdk/credential-provider-env": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.127.0.tgz", - "integrity": "sha512-Ig7XhUikRBlnRTYT5JBGzWfYZp68X5vkFVIFCmsHHt/qVy0Nz9raZpmDHicdS1u67yxDkWgCPn/bNevWnM0GFg==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3704,8 +3369,6 @@ }, "@aws-sdk/credential-provider-imds": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.127.0.tgz", - "integrity": "sha512-I6KlIBBzmJn/U1KikiC50PK3SspT9G5lkVLBaW5a6YfOcijqVTXfAN3kYzqhfeS0j4IgfJEwKVsjsZfmprJO5A==", "requires": { "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/property-provider": "3.127.0", @@ -3716,8 +3379,6 @@ }, "@aws-sdk/credential-provider-ini": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.142.0.tgz", - "integrity": "sha512-joMJTxUTNmxURnVmmd7XhtwOwijMjh7z09V8o2EHQMk+ak+rhaRgqb2kTA2nO0J3SRxdO5z5SKkyQgW0d1fY9g==", "requires": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -3731,8 +3392,6 @@ }, "@aws-sdk/credential-provider-node": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.142.0.tgz", - "integrity": "sha512-JkhCKNkEhCS2vgD/qg5hJPatupNLObqts9FXiDia5CF6w8YcHLH+mWSvhUMCUGkunAOvFHDkQL1uPXfoQuJvPg==", "requires": { "@aws-sdk/credential-provider-env": "3.127.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -3748,8 +3407,6 @@ }, "@aws-sdk/credential-provider-process": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.127.0.tgz", - "integrity": "sha512-6v0m2lqkO9J5fNlTl+HjriQNIdfg8mjVST544+5y9EnC/FVmTnIz64vfHveWdNkP/fehFx7wTimNENtoSqCn3A==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -3759,8 +3416,6 @@ }, "@aws-sdk/credential-provider-sso": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.142.0.tgz", - "integrity": "sha512-jJvp/A5xrikaeL0DhjhQTvUc0+eF0hN5Nbo+nxpnUOiOOkyqs329g65NI1TmLp/OzDcqQ/8p5vj2+7ufTGEOXQ==", "requires": { "@aws-sdk/client-sso": "3.142.0", "@aws-sdk/property-provider": "3.127.0", @@ -3771,8 +3426,6 @@ }, "@aws-sdk/credential-provider-web-identity": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.127.0.tgz", - "integrity": "sha512-85ahDZnLYB3dqkW+cQ0bWt+NVqOoxomTrJoq3IC2q6muebeFrJ0pyf0JEW/RNRzBiUvvsZujzGdWifzWyQKfVg==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3781,8 +3434,6 @@ }, "@aws-sdk/fetch-http-handler": { "version": "3.131.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.131.0.tgz", - "integrity": "sha512-eNxmPZQX2IUeBGWHNC7eNTekWn9VIPLYEMKJbKYUBJryxuTJ7TtLeyEK5oakUjMwP1AUvWT+CV7C+8L7uG1omQ==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/querystring-builder": "3.127.0", @@ -3793,8 +3444,6 @@ }, "@aws-sdk/hash-node": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.127.0.tgz", - "integrity": "sha512-wx7DKlXdKebH4JcMsOevdsm2oDNMVm36kuMm0XWRIrFWQ/oq7OquDpEMJzWvGqWF/IfFUpb7FhAWZZpALwlcwA==", "requires": { "@aws-sdk/types": "3.127.0", "@aws-sdk/util-buffer-from": "3.55.0", @@ -3803,8 +3452,6 @@ }, "@aws-sdk/invalid-dependency": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.127.0.tgz", - "integrity": "sha512-bxvmtmJ6gIRfOHvh1jAPZBH2mzppEblPjEOFo4mOzXz4U3qPIxeuukCjboMnGK9QEpV2wObWcYYld0vxoRrfiA==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3812,16 +3459,12 @@ }, "@aws-sdk/is-array-buffer": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", - "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/middleware-content-length": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.127.0.tgz", - "integrity": "sha512-AFmMaIEW3Rzg0TaKB9l/RENLowd7ZEEOpm0trYw1CgUUORWW/ydCsDT7pekPlC25CPbhUmWXCSA4xPFSYOVnDw==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3830,8 +3473,6 @@ }, "@aws-sdk/middleware-host-header": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.127.0.tgz", - "integrity": "sha512-e2gTLJb5lYP9lRV7hN3rKY2l4jv8OygOoHElZJ3Z8KPZskjHelYPcQ8XbdfhSXXxC3vc/0QqN0ResFt3W3Pplg==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3840,8 +3481,6 @@ }, "@aws-sdk/middleware-logger": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.127.0.tgz", - "integrity": "sha512-jMNLcZB/ECA7OfkNBLNeAlrLRehyfnUeNQJHW3kcxs9h1+6VxaF6wY+WKozszLI7/3OBzQrFHBQCfRZV7ykSLg==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3849,8 +3488,6 @@ }, "@aws-sdk/middleware-recursion-detection": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.127.0.tgz", - "integrity": "sha512-tB6WX+Z1kUKTnn5h38XFrTCzoqPKjUZLUjN4Wb27/cbeSiTSKGAZcCXHOJm36Ukorl5arlybQTqGe689EU00Hw==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3859,8 +3496,6 @@ }, "@aws-sdk/middleware-retry": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.127.0.tgz", - "integrity": "sha512-ZSvg/AyGUacWnf3i8ZbyImtiCH+NyafF8uV7bITP7JkwPrG+VdNocJZOr88GRM0c1A0jfkOf7+oq+fInPwwiNA==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/service-error-classification": "3.127.0", @@ -3872,8 +3507,6 @@ }, "@aws-sdk/middleware-serde": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.127.0.tgz", - "integrity": "sha512-xmWMYV/t9M+b9yHjqaD1noDNJJViI2QwOH7TQZ9VbbrvdVtDrFuS9Sf9He80TBCJqeHShwQN9783W1I3Pu/8kw==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3881,16 +3514,12 @@ }, "@aws-sdk/middleware-stack": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.127.0.tgz", - "integrity": "sha512-S1IoUE5o1vCmjsF5nIE8zlItNOM1UE+lhmZeigF7knXJ9+a6ewMB6POAj/s4eoi0wcn0eSnAGsqJCWMSUjOPLA==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/middleware-user-agent": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.127.0.tgz", - "integrity": "sha512-CHxgswoOzdkOEoIq7Oyob3Sx/4FYUv6BhUesAX7MNshaDDsTQPbSWjw5bqZDiL/gO+X/34fvqCVVpVD2GvxW/g==", "requires": { "@aws-sdk/protocol-http": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3899,8 +3528,6 @@ }, "@aws-sdk/node-config-provider": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.127.0.tgz", - "integrity": "sha512-bAHkASMhLZHT1yv2TX6OJGFV9Lc3t1gKfTMEKdXM2O2YhGfSx9A/qLeJm79oDfnILWQtSS2NicxlRDI2lYGf4g==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/shared-ini-file-loader": "3.127.0", @@ -3910,8 +3537,6 @@ }, "@aws-sdk/node-http-handler": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.127.0.tgz", - "integrity": "sha512-pyMKvheK8eDwWLgYIRsWy8wiyhsbYYcqkZQs3Eh6upI4E8iCY7eMmhWvHYCibvsO+UjsOwa4cAMOfwnv/Z9s8A==", "requires": { "@aws-sdk/abort-controller": "3.127.0", "@aws-sdk/protocol-http": "3.127.0", @@ -3922,8 +3547,6 @@ }, "@aws-sdk/property-provider": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.127.0.tgz", - "integrity": "sha512-JxenxlTEkWfLrtJqIjaXaJzAVQbbscoCb5bNjmdud07ESLVfWRKJx2nAJdecHKYp2M5NQyqBuFhQ1ELSFYQKCA==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3931,8 +3554,6 @@ }, "@aws-sdk/protocol-http": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.127.0.tgz", - "integrity": "sha512-UG83PVuKX40wilG2uRU0Fvz4OY8Bt+bSPOG776DFjwIXYzK7BwpJm9H2XI2HLhS5WxrJHhwrLBRgW6UiykMnFw==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" @@ -3940,8 +3561,6 @@ }, "@aws-sdk/querystring-builder": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.127.0.tgz", - "integrity": "sha512-tsoyp4lLPsASPDYWsezGAHD8VJsZbjUNATNAzTCFdH6p+4SKBK83Q5kfXCzxt13M+l3oKbxxIWLvS0kVQFyltQ==", "requires": { "@aws-sdk/types": "3.127.0", "@aws-sdk/util-uri-escape": "3.55.0", @@ -3950,30 +3569,22 @@ }, "@aws-sdk/querystring-parser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.127.0.tgz", - "integrity": "sha512-Vn/Dv+PqUSepp/DzLqq0LJJD8HdPefJCnLbO5WcHCARHSGlyGlZUFEM45k/oEHpTvgMXj/ORaP3A+tLwLu0AmA==", "requires": { "@aws-sdk/types": "3.127.0", "tslib": "^2.3.1" } }, "@aws-sdk/service-error-classification": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.127.0.tgz", - "integrity": "sha512-wjZY9rnlA8SPrICUumTYicEKtK4/yKB62iadUk66hxe8MrH8JhuHH2NqIad0Pt/bK/YtNVhd3yb4pRapOeY5qQ==" + "version": "3.127.0" }, "@aws-sdk/shared-ini-file-loader": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.127.0.tgz", - "integrity": "sha512-S3Nn4KRTqoJsB/TbRZSWBBUrkckNMR0Juqz7bOB+wupVvddKP6IcpspSC/GX9zgJjVMV8iGisZ6AUsYsC5r+cA==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/signature-v4": { "version": "3.130.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.130.0.tgz", - "integrity": "sha512-g5G1a1NHL2uOoFfC2zQdZcj+wbjgBQPkx6xGdtqNKf9v2kS0n6ap5JUGEaqWE02lUlmWHsoMsS73hXtzwXaBRQ==", "requires": { "@aws-sdk/is-array-buffer": "3.55.0", "@aws-sdk/types": "3.127.0", @@ -3985,8 +3596,6 @@ }, "@aws-sdk/smithy-client": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz", - "integrity": "sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA==", "requires": { "@aws-sdk/middleware-stack": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -3994,14 +3603,10 @@ } }, "@aws-sdk/types": { - "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.127.0.tgz", - "integrity": "sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og==" + "version": "3.127.0" }, "@aws-sdk/url-parser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.127.0.tgz", - "integrity": "sha512-njZ7zn41JHRpNfr3BCesVXCLZE0zcWSfEdtRV0ICw0cU1FgYcKELSuY9+gLUB4ci6uc7gq7mPE8+w30FcM4QeA==", "requires": { "@aws-sdk/querystring-parser": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -4010,16 +3615,12 @@ }, "@aws-sdk/util-base64-browser": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", - "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-base64-node": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", - "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", "requires": { "@aws-sdk/util-buffer-from": "3.55.0", "tslib": "^2.3.1" @@ -4027,24 +3628,18 @@ }, "@aws-sdk/util-body-length-browser": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz", - "integrity": "sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-body-length-node": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", - "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-buffer-from": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", - "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", "requires": { "@aws-sdk/is-array-buffer": "3.55.0", "tslib": "^2.3.1" @@ -4052,16 +3647,12 @@ }, "@aws-sdk/util-config-provider": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", - "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-defaults-mode-browser": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz", - "integrity": "sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w==", "requires": { "@aws-sdk/property-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -4071,8 +3662,6 @@ }, "@aws-sdk/util-defaults-mode-node": { "version": "3.142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz", - "integrity": "sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg==", "requires": { "@aws-sdk/config-resolver": "3.130.0", "@aws-sdk/credential-provider-imds": "3.127.0", @@ -4084,40 +3673,30 @@ }, "@aws-sdk/util-hex-encoding": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", - "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-locate-window": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", - "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-middleware": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.127.0.tgz", - "integrity": "sha512-EwAPPed9TNqh+Wov2VStLn2NuJ/Wyt7IkZCbCsBuSNp3BFZ1V4gfwTjqtKCtB2LQgQ48MTgWgNCvrH0zjCSPGg==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-uri-escape": { "version": "3.55.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", - "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-user-agent-browser": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.127.0.tgz", - "integrity": "sha512-uO2oHmJswuYKJS+GiMdYI8izhpC9M7/jFFvnAmLlTEVwpEi1VX9KePAOF+u5AaBC2kzITo/7dg141XfRHZloIQ==", "requires": { "@aws-sdk/types": "3.127.0", "bowser": "^2.11.0", @@ -4126,8 +3705,6 @@ }, "@aws-sdk/util-user-agent-node": { "version": "3.127.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.127.0.tgz", - "integrity": "sha512-3P/M4ZDD2qMeeoCk7TE/Mw7cG5IjB87F6BP8nI8/oHuaz7j6fsI7D49SNpyjl8JApRynZ122Ad6hwQwRj3isYw==", "requires": { "@aws-sdk/node-config-provider": "3.127.0", "@aws-sdk/types": "3.127.0", @@ -4136,16 +3713,12 @@ }, "@aws-sdk/util-utf8-browser": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", - "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", "requires": { "tslib": "^2.3.1" } }, "@aws-sdk/util-utf8-node": { "version": "3.109.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", - "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", "requires": { "@aws-sdk/util-buffer-from": "3.55.0", "tslib": "^2.3.1" @@ -4153,8 +3726,6 @@ }, "@babel/code-frame": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "requires": { "@babel/highlight": "^7.18.6" @@ -4162,14 +3733,10 @@ }, "@babel/compat-data": { "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", "dev": true }, "@babel/core": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", @@ -4191,8 +3758,6 @@ }, "@babel/generator": { "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", "dev": true, "requires": { "@babel/types": "^7.18.10", @@ -4202,8 +3767,6 @@ "dependencies": { "@jridgewell/gen-mapping": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -4215,8 +3778,6 @@ }, "@babel/helper-compilation-targets": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", "dev": true, "requires": { "@babel/compat-data": "^7.18.8", @@ -4227,14 +3788,10 @@ }, "@babel/helper-environment-visitor": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true }, "@babel/helper-function-name": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", "dev": true, "requires": { "@babel/template": "^7.18.6", @@ -4243,8 +3800,6 @@ }, "@babel/helper-hoist-variables": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "requires": { "@babel/types": "^7.18.6" @@ -4252,8 +3807,6 @@ }, "@babel/helper-module-imports": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, "requires": { "@babel/types": "^7.18.6" @@ -4261,8 +3814,6 @@ }, "@babel/helper-module-transforms": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", @@ -4277,8 +3828,6 @@ }, "@babel/helper-simple-access": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", "dev": true, "requires": { "@babel/types": "^7.18.6" @@ -4286,8 +3835,6 @@ }, "@babel/helper-split-export-declaration": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "requires": { "@babel/types": "^7.18.6" @@ -4295,26 +3842,18 @@ }, "@babel/helper-string-parser": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", "dev": true }, "@babel/helper-validator-identifier": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", "dev": true }, "@babel/helper-validator-option": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true }, "@babel/helpers": { "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", "dev": true, "requires": { "@babel/template": "^7.18.6", @@ -4324,8 +3863,6 @@ }, "@babel/highlight": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.18.6", @@ -4335,8 +3872,6 @@ "dependencies": { "ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -4344,8 +3879,6 @@ }, "chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -4355,8 +3888,6 @@ }, "color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" @@ -4364,26 +3895,18 @@ }, "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -4393,14 +3916,10 @@ }, "@babel/parser": { "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true }, "@babel/template": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", @@ -4410,8 +3929,6 @@ }, "@babel/traverse": { "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", @@ -4428,8 +3945,6 @@ }, "@babel/types": { "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.18.10", @@ -4439,8 +3954,6 @@ }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", @@ -4452,8 +3965,6 @@ "dependencies": { "argparse": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -4461,14 +3972,10 @@ }, "camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -4477,8 +3984,6 @@ }, "js-yaml": { "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -4487,8 +3992,6 @@ }, "locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -4496,8 +3999,6 @@ }, "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -4505,8 +4006,6 @@ }, "p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -4516,14 +4015,10 @@ }, "@istanbuljs/schema": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jridgewell/gen-mapping": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.0", @@ -4532,26 +4027,18 @@ }, "@jridgewell/resolve-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -4560,14 +4047,10 @@ }, "@ungap/promise-all-settled": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, "aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -4576,20 +4059,14 @@ }, "ansi-colors": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" @@ -4597,8 +4074,6 @@ }, "anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -4607,8 +4082,6 @@ }, "append-transform": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "requires": { "default-require-extensions": "^3.0.0" @@ -4616,31 +4089,21 @@ }, "archy": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "assertion-error": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "version": "0.4.0" }, "axios": { "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", "requires": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" @@ -4648,25 +4111,17 @@ }, "balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + "version": "2.11.0" }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -4675,8 +4130,6 @@ }, "braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -4684,14 +4137,10 @@ }, "browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "browserslist": { "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", "dev": true, "requires": { "caniuse-lite": "^1.0.30001370", @@ -4702,8 +4151,6 @@ }, "caching-transform": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { "hasha": "^5.0.0", @@ -4714,20 +4161,14 @@ }, "camelcase": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "caniuse-lite": { "version": "1.0.30001374", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", - "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", "dev": true }, "chai": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -4741,8 +4182,6 @@ }, "chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -4751,8 +4190,6 @@ "dependencies": { "supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -4762,14 +4199,10 @@ }, "check-error": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -4784,14 +4217,10 @@ }, "clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -4801,8 +4230,6 @@ }, "color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" @@ -4810,34 +4237,24 @@ }, "color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } }, "commondir": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "convert-source-map": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -4845,16 +4262,12 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true } } }, "cross-spawn": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -4864,8 +4277,6 @@ }, "debug": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -4873,22 +4284,16 @@ "dependencies": { "ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "deep-eql": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -4896,64 +4301,44 @@ }, "default-require-extensions": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, "requires": { "strip-bom": "^4.0.0" } }, "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "version": "1.0.0" }, "diff": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "electron-to-chromium": { "version": "1.4.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", - "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", "dev": true }, "emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "es6-error": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, "escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -4961,8 +4346,6 @@ }, "find-cache-dir": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "requires": { "commondir": "^1.0.1", @@ -4972,8 +4355,6 @@ }, "find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { "locate-path": "^6.0.0", @@ -4982,19 +4363,13 @@ }, "flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + "version": "1.15.1" }, "foreground-child": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -5003,8 +4378,6 @@ }, "form-data": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5013,44 +4386,30 @@ }, "fromentries": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-func-name": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, "glob": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -5063,8 +4422,6 @@ "dependencies": { "minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5074,8 +4431,6 @@ }, "glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -5083,32 +4438,22 @@ }, "globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "hasha": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { "is-stream": "^2.0.0", @@ -5117,32 +4462,22 @@ }, "he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "html-escaper": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -5151,14 +4486,10 @@ }, "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -5166,20 +4497,14 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -5187,56 +4512,38 @@ }, "is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "is-windows": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "istanbul-lib-coverage": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, "istanbul-lib-hook": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { "append-transform": "^2.0.0" @@ -5244,8 +4551,6 @@ }, "istanbul-lib-instrument": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { "@babel/core": "^7.7.5", @@ -5256,8 +4561,6 @@ }, "istanbul-lib-processinfo": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "requires": { "archy": "^1.0.0", @@ -5270,8 +4573,6 @@ }, "istanbul-lib-report": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", @@ -5281,8 +4582,6 @@ "dependencies": { "supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5292,8 +4591,6 @@ }, "istanbul-lib-source-maps": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -5303,8 +4600,6 @@ }, "istanbul-reports": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -5313,14 +4608,10 @@ }, "js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -5328,26 +4619,18 @@ }, "jsesc": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "json5": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true }, "locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { "p-locate": "^5.0.0" @@ -5355,20 +4638,14 @@ }, "lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.flattendeep": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -5377,8 +4654,6 @@ }, "loupe": { "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "requires": { "get-func-name": "^2.0.0" @@ -5386,30 +4661,22 @@ }, "make-dir": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { "semver": "^6.0.0" } }, "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "version": "1.52.0" }, "mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } }, "minimatch": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5417,8 +4684,6 @@ }, "mocha": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -5449,20 +4714,14 @@ }, "ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "nanoid": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "nock": { "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", "dev": true, "requires": { "debug": "^4.1.0", @@ -5473,8 +4732,6 @@ }, "node-preload": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, "requires": { "process-on-spawn": "^1.0.0" @@ -5482,20 +4739,14 @@ }, "node-releases": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, "normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "nyc": { "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "requires": { "@istanbuljs/load-nyc-config": "^1.0.0", @@ -5529,14 +4780,10 @@ "dependencies": { "camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "cliui": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", @@ -5546,14 +4793,10 @@ }, "decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -5562,8 +4805,6 @@ }, "locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -5571,8 +4812,6 @@ }, "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -5580,8 +4819,6 @@ }, "p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -5589,8 +4826,6 @@ }, "wrap-ansi": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -5600,14 +4835,10 @@ }, "y18n": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -5625,8 +4856,6 @@ }, "yargs-parser": { "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -5637,8 +4866,6 @@ }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -5646,8 +4873,6 @@ }, "p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { "yocto-queue": "^0.1.0" @@ -5655,8 +4880,6 @@ }, "p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { "p-limit": "^3.0.2" @@ -5664,8 +4887,6 @@ }, "p-map": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" @@ -5673,14 +4894,10 @@ }, "p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "package-hash": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "requires": { "graceful-fs": "^4.1.15", @@ -5691,44 +4908,30 @@ }, "path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "pathval": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pkg-dir": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" @@ -5736,8 +4939,6 @@ "dependencies": { "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", @@ -5746,8 +4947,6 @@ }, "locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" @@ -5755,8 +4954,6 @@ }, "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -5764,8 +4961,6 @@ }, "p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" @@ -5775,14 +4970,10 @@ }, "prettier": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true }, "process-on-spawn": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", "dev": true, "requires": { "fromentries": "^1.2.0" @@ -5790,14 +4981,10 @@ }, "propagate": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, "randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" @@ -5805,8 +4992,6 @@ }, "readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -5814,8 +4999,6 @@ }, "release-zalgo": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { "es6-error": "^4.0.1" @@ -5823,26 +5006,18 @@ }, "require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -5850,20 +5025,14 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "semver": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "serialize-javascript": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -5871,14 +5040,10 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" @@ -5886,26 +5051,18 @@ }, "shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "spawn-wrap": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { "foreground-child": "^2.0.0", @@ -5918,14 +5075,10 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -5935,8 +5088,6 @@ }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -5944,20 +5095,14 @@ }, "strip-bom": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, "strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5965,8 +5110,6 @@ }, "test-exclude": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { "@istanbuljs/schema": "^0.1.2", @@ -5976,8 +5119,6 @@ "dependencies": { "minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5987,40 +5128,28 @@ }, "to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.0" }, "type-detect": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "type-fest": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, "typedarray-to-buffer": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, "requires": { "is-typedarray": "^1.0.0" @@ -6028,8 +5157,6 @@ }, "update-browserslist-db": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -6037,14 +5164,10 @@ } }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "version": "8.3.2" }, "which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -6052,20 +5175,14 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "workerpool": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -6075,14 +5192,10 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write-file-atomic": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { "imurmurhash": "^0.1.4", @@ -6093,14 +5206,10 @@ }, "y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -6114,14 +5223,10 @@ }, "yargs-parser": { "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { "camelcase": "^6.0.0", @@ -6132,8 +5237,6 @@ }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/package.json b/package.json index 70fff42b..605ad11b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "prettier": "prettier -c src tests", "prettier:fix": "prettier -cw src tests", "test": "mocha --require tests/test-helpers --recursive tests/unit", - "test:coverage": "nyc --check-coverage true npm test" + "test:coverage": "nyc --check-coverage=true npm test" }, "devDependencies": { "chai": "^4.2.0", diff --git a/tests/fixtures/mocks/missing-work-1234.json b/tests/fixtures/mocks/missing-work-1234.json index 6dd41a07..c968e85b 100644 --- a/tests/fixtures/mocks/missing-work-1234.json +++ b/tests/fixtures/mocks/missing-work-1234.json @@ -2,12 +2,5 @@ "_index": "dev-dc-v2-work", "_type": "_doc", "_id": "1234", - "_version": 1, - "found": true, - "_source": { - "id": "1234", - "api_model": "Work", - "published": false, - "visibility": "Public" - } + "found": false } diff --git a/tests/fixtures/mocks/unpublished-work-1234.json b/tests/fixtures/mocks/unpublished-work-1234.json new file mode 100644 index 00000000..6dd41a07 --- /dev/null +++ b/tests/fixtures/mocks/unpublished-work-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": false, + "visibility": "Public" + } +} diff --git a/tests/unit/api/opensearch.test.js b/tests/unit/api/opensearch.test.js index 83d676a3..30d9f52a 100644 --- a/tests/unit/api/opensearch.test.js +++ b/tests/unit/api/opensearch.test.js @@ -22,7 +22,18 @@ describe("getWork()", function () { it("returns 404 Not Found for unpublished works", async function () { mock .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + .reply(200, helpers.testFixture("mocks/unpublished-work-1234.json")); + + const result = await opensearch.getWork("1234"); + const body = JSON.parse(result.body); + expect(result.statusCode).to.eq(404); + expect(body.found).to.eq(false); + }); + + it("returns 404 Not Found for missing documents", async function () { + mock + .get("/dc-v2-work/_doc/1234") + .reply(404, helpers.testFixture("mocks/missing-work-1234.json")); const result = await opensearch.getWork("1234"); const body = JSON.parse(result.body); From 4638c8ed58b6a603d86ea08356e520437cf23ce5 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 10 Aug 2022 17:20:20 +0000 Subject: [PATCH 04/61] Use an API Gateway v2 HTTP API instead of a v1 REST API --- template.yaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/template.yaml b/template.yaml index d2ceb40b..42d29eea 100644 --- a/template.yaml +++ b/template.yaml @@ -43,7 +43,7 @@ Resources: ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: - Type: Api + Type: HttpApi Properties: RestApiId: !Ref dcApi Path: /collections/{id} @@ -72,7 +72,7 @@ Resources: ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: - Type: Api + Type: HttpApi Properties: RestApiId: !Ref dcApi Path: /file-sets/{id} @@ -101,7 +101,7 @@ Resources: ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: - Type: Api + Type: HttpApi Properties: RestApiId: !Ref dcApi Path: /works/{id} @@ -130,20 +130,18 @@ Resources: ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: SearchApi: - Type: Api + Type: HttpApi Properties: RestApiId: !Ref dcApi Path: /search Method: POST SearchWithModelsApi: - Type: Api + Type: HttpApi Properties: RestApiId: !Ref dcApi Path: /search/{models} Method: POST dcApi: - Type: AWS::Serverless::Api + Type: AWS::Serverless::HttpApi Properties: - EndpointConfiguration: - Type: REGIONAL StageName: v2 From 33e87b86be3aaa2d97cc30eabe59c2750517dd0c Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 10 Aug 2022 19:21:02 +0000 Subject: [PATCH 05/61] Change RestApiId to ApiId in template --- template.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/template.yaml b/template.yaml index 42d29eea..58968670 100644 --- a/template.yaml +++ b/template.yaml @@ -45,7 +45,7 @@ Resources: Api: Type: HttpApi Properties: - RestApiId: !Ref dcApi + ApiId: !Ref dcApi Path: /collections/{id} Method: GET getFileSetByIdFunction: @@ -74,7 +74,7 @@ Resources: Api: Type: HttpApi Properties: - RestApiId: !Ref dcApi + ApiId: !Ref dcApi Path: /file-sets/{id} Method: GET getWorkByIdFunction: @@ -103,7 +103,7 @@ Resources: Api: Type: HttpApi Properties: - RestApiId: !Ref dcApi + ApiId: !Ref dcApi Path: /works/{id} Method: GET searchFunction: @@ -132,13 +132,13 @@ Resources: SearchApi: Type: HttpApi Properties: - RestApiId: !Ref dcApi + ApiId: !Ref dcApi Path: /search Method: POST SearchWithModelsApi: Type: HttpApi Properties: - RestApiId: !Ref dcApi + ApiId: !Ref dcApi Path: /search/{models} Method: POST dcApi: From b6cd7e8ad0117e7acaca383dd410d19cd4405a59 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Fri, 12 Aug 2022 19:41:36 +0000 Subject: [PATCH 06/61] Enable CORS on API --- template.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/template.yaml b/template.yaml index 58968670..48b89cb1 100644 --- a/template.yaml +++ b/template.yaml @@ -144,4 +144,14 @@ Resources: dcApi: Type: AWS::Serverless::HttpApi Properties: + CorsConfiguration: + AllowOrigins: + - "*" + AllowHeaders: + - "*" + AllowMethods: + - GET + - POST + - OPTIONS + MaxAge: 600 StageName: v2 From c1b375b10f771c48db4edaeb9679b834f5cf25a8 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 15 Aug 2022 16:00:36 +0000 Subject: [PATCH 07/61] Factor model checking out of handler and add tests --- .github/workflows/build.yml | 5 +- README.md | 2 + nyc.config.js | 9 + package.json | 2 +- src/api/request/models.js | 21 ++ src/api/request/pipeline.js | 15 +- src/handlers/search.js | 2 +- tests/fixtures/mocks/missing-index.json | 21 ++ .../mocks/search-multiple-targets.json | 10 +- tests/fixtures/mocks/search.json | 192 +++++++++--------- tests/test-helpers.js | 5 +- tests/unit/api/opensearch.test.js | 4 +- tests/unit/api/request/models.test.js | 29 +++ tests/unit/api/request/pipeline.test.js | 42 ++++ tests/unit/api/response/opensearch.test.js | 45 ++++ tests/unit/aws/environment.test.js | 4 +- 16 files changed, 290 insertions(+), 118 deletions(-) create mode 100644 nyc.config.js create mode 100644 src/api/request/models.js create mode 100644 tests/fixtures/mocks/missing-index.json create mode 100644 tests/unit/api/request/models.test.js create mode 100644 tests/unit/api/request/pipeline.test.js create mode 100644 tests/unit/api/response/opensearch.test.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 466db3a4..26ee1f64 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Digital Collections API CI +name: Digital Collections API on: - push jobs: @@ -9,8 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Use Node.js 16.x - uses: actions/setup-node@v3 + - uses: actions/setup-node@v3 with: node-version: 16.x cache: 'npm' diff --git a/README.md b/README.md index c5fe290b..3146c9fe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # dc-api-v2 +![Build Status](https://github.com/nulib/dc-api-v2/actions/workflows/build.yml/badge.svg) + ## Directory structure ``` diff --git a/nyc.config.js b/nyc.config.js new file mode 100644 index 00000000..1e8f0209 --- /dev/null +++ b/nyc.config.js @@ -0,0 +1,9 @@ +'use strict'; + +const defaultExclude = require('@istanbuljs/schema/default-exclude'); +const localExclude = ["src/handlers/**"]; +module.exports = { + all: true, + "check-coverage": true, + exclude: defaultExclude.concat(localExclude) +}; diff --git a/package.json b/package.json index 605ad11b..11dbe541 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "prettier": "prettier -c src tests", "prettier:fix": "prettier -cw src tests", "test": "mocha --require tests/test-helpers --recursive tests/unit", - "test:coverage": "nyc --check-coverage=true npm test" + "test:coverage": "nyc npm test" }, "devDependencies": { "chai": "^4.2.0", diff --git a/src/api/request/models.js b/src/api/request/models.js new file mode 100644 index 00000000..96692475 --- /dev/null +++ b/src/api/request/models.js @@ -0,0 +1,21 @@ +const { prefix } = require("../../aws/environment"); + +const mapTargets = { + works: "dc-v2-work", + "file-sets": "dc-v2-file-set", + collections: "dc-v2-collection", +}; + +function validModels(models) { + return models.every(isAllowed); +} + +function isAllowed(model) { + return mapTargets.hasOwnProperty(model); +} + +function modelsToTargets(models) { + return String(models.map((model) => prefix(mapTargets[model]))); +} + +module.exports = { modelsToTargets, validModels }; diff --git a/src/api/request/pipeline.js b/src/api/request/pipeline.js index 187da6ad..c825e57b 100644 --- a/src/api/request/pipeline.js +++ b/src/api/request/pipeline.js @@ -1,28 +1,31 @@ module.exports = class RequestPipeline { - constructor(body) { - this.body = JSON.parse(body); + constructor(searchContext) { + this.searchContext = { ...searchContext }; + this.searchContext.query ||= { match_all: {} }; } // Things tranformer needs to do: // - not allow unpuplished or restricted items // - Reading room/IP (not in first iteration) - // - Add `track_total_hits` to body of search (so we can get accurate hits.total.value) + // - Add `track_total_hits` to search context (so we can get accurate hits.total.value) authFilter() { - const matchTheQuery = this.body.query || { match_all: {} }; + const matchTheQuery = this.searchContext.query; const beUnpublished = { term: { published: false } }; const beRestricted = { term: { visibility: "Private" } }; - this.body.query = { + this.searchContext.query = { bool: { must: [matchTheQuery], must_not: [beUnpublished, beRestricted], }, }; + this.searchContext.track_total_hits = true; + return this; } toJson() { - return JSON.stringify(this.body); + return JSON.stringify(this.searchContext); } }; diff --git a/src/handlers/search.js b/src/handlers/search.js index 21f04b19..8097bda8 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,4 +1,4 @@ -const { prefix } = require("../aws/environment"); +const { modelsToTargets, validModels } = require("../api/request/models"); const { search } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); const RequestPipeline = require("../api/request/pipeline.js"); diff --git a/tests/fixtures/mocks/missing-index.json b/tests/fixtures/mocks/missing-index.json new file mode 100644 index 00000000..5320801b --- /dev/null +++ b/tests/fixtures/mocks/missing-index.json @@ -0,0 +1,21 @@ +{ + "error": { + "root_cause": [ + { + "type": "index_not_found_exception", + "reason": "no such index [non-existent-index]", + "index": "non-existent-index", + "resource.id": "non-existent-index", + "resource.type": "index_or_alias", + "index_uuid": "_na_" + } + ], + "type": "index_not_found_exception", + "reason": "no such index [non-existent-index]", + "index": "non-existent-index", + "resource.id": "non-existent-index", + "resource.type": "index_or_alias", + "index_uuid": "_na_" + }, + "status": 404 +} diff --git a/tests/fixtures/mocks/search-multiple-targets.json b/tests/fixtures/mocks/search-multiple-targets.json index c8a9af7d..596b9776 100644 --- a/tests/fixtures/mocks/search-multiple-targets.json +++ b/tests/fixtures/mocks/search-multiple-targets.json @@ -53,7 +53,7 @@ "admin_email": null, "indexed_at": "2022-07-28T15:43:05.103729", "api_model": "Collection", - "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1410", + "finding_aid_url": "https://findingaids.test.library.northwestern.edu/repositories/7/resources/1410", "api_link": "[PLACEHOLDER]/35c3a71a-ce83-4410-82ec-640e01da86eb", "id": "35c3a71a-ce83-4410-82ec-640e01da86eb", "representative_image": {}, @@ -168,7 +168,7 @@ "admin_email": null, "indexed_at": "2022-07-28T15:43:05.105084", "api_model": "Collection", - "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1786", + "finding_aid_url": "https://findingaids.test.library.northwestern.edu/repositories/7/resources/1786", "api_link": "[PLACEHOLDER]/ec153e6d-a543-4b50-a7dc-ae5e98a27129", "id": "ec153e6d-a543-4b50-a7dc-ae5e98a27129", "representative_image": {}, @@ -191,7 +191,7 @@ "admin_email": null, "indexed_at": "2022-07-28T15:43:05.105157", "api_model": "Collection", - "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1245", + "finding_aid_url": "https://findingaids.test.library.northwestern.edu/repositories/6/resources/1245", "api_link": "[PLACEHOLDER]/450b857e-2e74-491e-9ef1-2c73e49f0c16", "id": "450b857e-2e74-491e-9ef1-2c73e49f0c16", "representative_image": {}, @@ -214,7 +214,7 @@ "admin_email": null, "indexed_at": "2022-07-28T15:43:05.141902", "api_model": "Collection", - "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1207", + "finding_aid_url": "https://findingaids.test.library.northwestern.edu/repositories/7/resources/1207", "api_link": "[PLACEHOLDER]/3121f8ee-5265-4b19-bae3-59f96e9ac01a", "id": "3121f8ee-5265-4b19-bae3-59f96e9ac01a", "representative_image": {}, @@ -237,7 +237,7 @@ "admin_email": null, "indexed_at": "2022-07-28T15:43:05.142115", "api_model": "Collection", - "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1412", + "finding_aid_url": "https://findingaids.test.library.northwestern.edu/repositories/6/resources/1412", "api_link": "[PLACEHOLDER]/dc70f756-8e30-4724-b50d-591843db2537", "id": "dc70f756-8e30-4724-b50d-591843db2537", "representative_image": {}, diff --git a/tests/fixtures/mocks/search.json b/tests/fixtures/mocks/search.json index b421e1c4..ea651f4c 100644 --- a/tests/fixtures/mocks/search.json +++ b/tests/fixtures/mocks/search.json @@ -33,7 +33,7 @@ "box_number": [], "thumbnail": null, "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/9a/2a/8a/f3/-4/d0/4-/4b/ae/-a/d0/a-/71/1c/55/04/be/84-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/iiif3/9a/2a/8a/f3/-4/d0/4-/4b/ae/-a/d0/a-/71/1c/55/04/be/84-manifest.json", "work_type": "Audio", "collection": {}, "published": false, @@ -85,7 +85,7 @@ "box_number": [], "thumbnail": null, "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/5a/e1/b2/39/-3/09/d-/4e/36/-9/e0/4-/05/60/6e/27/d6/37-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/iiif3/5a/e1/b2/39/-3/09/d-/4e/36/-9/e0/4-/05/60/6e/27/d6/37-manifest.json", "work_type": "Video", "collection": {}, "published": false, @@ -107,22 +107,22 @@ }, { "role": "Access", - "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/aa/5c/8b/63/-2/95/8-/48/e8/-8/92/a-/32/93/ea/17/a2/04/aa5c8b63-2958-48e8-892a-3293ea17a204.m3u8", + "streaming_url": "https://assets.test.library.northwestern.edu.edu/aa/5c/8b/63/-2/95/8-/48/e8/-8/92/a-/32/93/ea/17/a2/04/aa5c8b63-2958-48e8-892a-3293ea17a204.m3u8", "mime_type": "video/x-m4v", "rank": 1073741824, "id": "aa5c8b63-2958-48e8-892a-3293ea17a204", "label": "adfkjasld;kfjal;ks", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/posters/aa5c8b63-2958-48e8-892a-3293ea17a204", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/posters/aa5c8b63-2958-48e8-892a-3293ea17a204", "poster_offset": 5572 }, { "role": "Access", - "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/fd/e2/e7/0c/-b/98/4-/47/27/-8/5d/b-/a9/62/d2/ad/5a/ba/fde2e70c-b984-4727-85db-a962d2ad5aba.m3u8", + "streaming_url": "https://assets.test.library.northwestern.edu.edu/fd/e2/e7/0c/-b/98/4-/47/27/-8/5d/b-/a9/62/d2/ad/5a/ba/fde2e70c-b984-4727-85db-a962d2ad5aba.m3u8", "mime_type": "video/mp4", "rank": 0, "id": "fde2e70c-b984-4727-85db-a962d2ad5aba", "label": "mp4", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/posters/fde2e70c-b984-4727-85db-a962d2ad5aba", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/posters/fde2e70c-b984-4727-85db-a962d2ad5aba", "poster_offset": 96371 }, { @@ -208,7 +208,7 @@ "box_number": [], "thumbnail": null, "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/00/81/d3/de/-6/0c/0-/41/f8/-8/0f/b-/9e/57/fa/23/e9/20-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/iiif3/00/81/d3/de/-6/0c/0-/41/f8/-8/0f/b-/9e/57/fa/23/e9/20-manifest.json", "work_type": "Video", "collection": {}, "published": false, @@ -220,7 +220,7 @@ "file_sets": [ { "role": "Access", - "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/df/53/6c/a3/-c/55/8-/4c/0d/-9/6f/9-/b9/1a/6f/8a/32/5c/df536ca3-c558-4c0d-96f9-b91a6f8a325c.m3u8", + "streaming_url": "https://assets.test.library.northwestern.edu.edu/df/53/6c/a3/-c/55/8-/4c/0d/-9/6f/9-/b9/1a/6f/8a/32/5c/df536ca3-c558-4c0d-96f9-b91a6f8a325c.m3u8", "mime_type": "video/quicktime", "rank": 1073741824, "id": "df536ca3-c558-4c0d-96f9-b91a6f8a325c", @@ -240,7 +240,7 @@ }, { "role": "Access", - "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/76/bb/80/04/-2/45/5-/4a/16/-9/b1/9-/12/93/8d/20/48/38/76bb8004-2455-4a16-9b19-12938d204838.m3u8", + "streaming_url": "https://assets.test.library.northwestern.edu.edu/76/bb/80/04/-2/45/5-/4a/16/-9/b1/9-/12/93/8d/20/48/38/76bb8004-2455-4a16-9b19-12938d204838.m3u8", "mime_type": "video/mp4", "rank": 0, "id": "76bb8004-2455-4a16-9b19-12938d204838", @@ -291,7 +291,7 @@ "box_number": [], "thumbnail": null, "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/iiif3/25/a1/b8/00/-c/ab/0-/42/00/-a/bb/b-/90/15/84/2a/7b/29-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/iiif3/25/a1/b8/00/-c/ab/0-/42/00/-a/bb/b-/90/15/84/2a/7b/29-manifest.json", "work_type": "Video", "collection": {}, "published": false, @@ -303,7 +303,7 @@ "file_sets": [ { "role": "Access", - "streaming_url": "https://meadow-streaming.rdc-staging.library.northwestern.edu/a2/b2/28/c7/-6/8f/8-/45/0d/-9/e2/c-/21/f3/96/86/47/63/a2b228c7-68f8-450d-9e2c-21f396864763.m3u8", + "streaming_url": "https://assets.test.library.northwestern.edu.edu/a2/b2/28/c7/-6/8f/8-/45/0d/-9/e2/c-/21/f3/96/86/47/63/a2b228c7-68f8-450d-9e2c-21f396864763.m3u8", "mime_type": "video/mp4", "rank": 0, "id": "a2b228c7-68f8-450d-9e2c-21f396864763", @@ -352,9 +352,9 @@ "create_date": "2022-06-27T21:14:37.734614Z", "identifier": [], "box_number": [], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702/full/!300,300/0/default.jpg", "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/2d/83/93/43/-b/45/9-/4b/27/-8/3b/e-/0b/c4/03/25/88/de-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/2d/83/93/43/-b/45/9-/4b/27/-8/3b/e-/0b/c4/03/25/88/de-manifest.json", "work_type": "Image", "collection": {}, "published": false, @@ -371,7 +371,7 @@ "rank": 0, "id": "f0591ed1-6a0a-4baf-a983-4b854800a702", "label": "label", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702", "poster_offset": null }, { @@ -400,7 +400,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "f0591ed1-6a0a-4baf-a983-4b854800a702", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/f0591ed1-6a0a-4baf-a983-4b854800a702" }, "api_model": "Work", "physical_description_material": [], @@ -428,9 +428,9 @@ "create_date": "2021-11-29T17:03:48.699230Z", "identifier": [], "box_number": ["Box 84"], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89/full/!300,300/0/default.jpg", "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ee/59/24/40/-4/40/1-/44/5b/-8/b8/d-/29/91/b2/81/7e/57-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/ee/59/24/40/-4/40/1-/44/5b/-8/b8/d-/29/91/b2/81/7e/57-manifest.json", "work_type": "Image", "collection": { "id": "fd9927b0-6cd4-4aa7-bf86-ca84955980a6", @@ -450,7 +450,7 @@ "rank": 1360072977, "id": "6145c11d-868a-4974-acb3-c51ae059ea59", "label": "P0413_B84_F04_009_09_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/6145c11d-868a-4974-acb3-c51ae059ea59", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/6145c11d-868a-4974-acb3-c51ae059ea59", "poster_offset": null }, { @@ -460,7 +460,7 @@ "rank": -1216907400, "id": "62418d3f-3777-424f-8a91-5030e78df746", "label": "P0413_B84_F04_009_03_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/62418d3f-3777-424f-8a91-5030e78df746", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/62418d3f-3777-424f-8a91-5030e78df746", "poster_offset": null }, { @@ -470,7 +470,7 @@ "rank": -787410671, "id": "633b2e85-209e-48eb-b22d-5fbb061d0562", "label": "P0413_B84_F04_009_05_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/633b2e85-209e-48eb-b22d-5fbb061d0562", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/633b2e85-209e-48eb-b22d-5fbb061d0562", "poster_offset": null }, { @@ -480,7 +480,7 @@ "rank": 572662306, "id": "6e35c386-395c-4071-9fb5-54d6b130115f", "label": "P0413_B84_F04_009_07_07.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/6e35c386-395c-4071-9fb5-54d6b130115f", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/6e35c386-395c-4071-9fb5-54d6b130115f", "poster_offset": null }, { @@ -490,7 +490,7 @@ "rank": 1073741824, "id": "78dc79de-cb76-44b5-8010-d993056fe9d2", "label": "P0413_B84_F04_009_08_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/78dc79de-cb76-44b5-8010-d993056fe9d2", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/78dc79de-cb76-44b5-8010-d993056fe9d2", "poster_offset": null }, { @@ -500,7 +500,7 @@ "rank": 0, "id": "797e081e-e798-48ee-bbc1-0ca6c619de97", "label": "P0413_B84_F04_009_06_07.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/797e081e-e798-48ee-bbc1-0ca6c619de97", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/797e081e-e798-48ee-bbc1-0ca6c619de97", "poster_offset": null }, { @@ -510,7 +510,7 @@ "rank": 214748365, "id": "820aa4ff-4c7e-49ba-8824-738a01106f14", "label": "P0413_B84_F04_009_07_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/820aa4ff-4c7e-49ba-8824-738a01106f14", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/820aa4ff-4c7e-49ba-8824-738a01106f14", "poster_offset": null }, { @@ -520,7 +520,7 @@ "rank": 1216907400, "id": "82b1761f-d709-454e-8fb0-2f0ad661cb7f", "label": "P0413_B84_F04_009_08_08.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/82b1761f-d709-454e-8fb0-2f0ad661cb7f", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/82b1761f-d709-454e-8fb0-2f0ad661cb7f", "poster_offset": null }, { @@ -530,7 +530,7 @@ "rank": 1574821341, "id": "8325efc6-8e3f-4b98-b979-99ceb05aa48f", "label": "P0413_B84_F04_009_10_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8325efc6-8e3f-4b98-b979-99ceb05aa48f", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/8325efc6-8e3f-4b98-b979-99ceb05aa48f", "poster_offset": null }, { @@ -540,7 +540,7 @@ "rank": 2004318071, "id": "87b8da22-a06a-45c8-ad05-5eeb5732f5be", "label": "P0413_B84_F04_009_10_07.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/87b8da22-a06a-45c8-ad05-5eeb5732f5be", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/87b8da22-a06a-45c8-ad05-5eeb5732f5be", "poster_offset": null }, { @@ -550,7 +550,7 @@ "rank": -1646404130, "id": "88987e7a-3277-4ffd-8fce-bdbb61eccfdb", "label": "P0413_B84_F04_009_02_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/88987e7a-3277-4ffd-8fce-bdbb61eccfdb", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/88987e7a-3277-4ffd-8fce-bdbb61eccfdb", "poster_offset": null }, { @@ -560,7 +560,7 @@ "rank": 1789569706, "id": "893523db-d478-4501-9651-149c90e943c5", "label": "P0413_B84_F04_009_10_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/893523db-d478-4501-9651-149c90e943c5", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/893523db-d478-4501-9651-149c90e943c5", "poster_offset": null }, { @@ -570,7 +570,7 @@ "rank": 1646404130, "id": "8b5362e6-cfa9-4c4d-80ee-82978adf6977", "label": "P0413_B84_F04_009_10_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8b5362e6-cfa9-4c4d-80ee-82978adf6977", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/8b5362e6-cfa9-4c4d-80ee-82978adf6977", "poster_offset": null }, { @@ -580,7 +580,7 @@ "rank": -2004318071, "id": "8ccde0c9-9749-45a6-b087-fe7369a2ae73", "label": "P0413_B84_F04_009_02_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8ccde0c9-9749-45a6-b087-fe7369a2ae73", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/8ccde0c9-9749-45a6-b087-fe7369a2ae73", "poster_offset": null }, { @@ -590,7 +590,7 @@ "rank": 286331153, "id": "8f401490-5e19-4209-b18e-e701937f3acf", "label": "P0413_B84_F04_009_07_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/8f401490-5e19-4209-b18e-e701937f3acf", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/8f401490-5e19-4209-b18e-e701937f3acf", "poster_offset": null }, { @@ -600,7 +600,7 @@ "rank": -1431655765, "id": "95353321-bc0d-4ea1-b2b9-49f6e4ef6314", "label": "P0413_B84_F04_009_02_09.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/95353321-bc0d-4ea1-b2b9-49f6e4ef6314", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/95353321-bc0d-4ea1-b2b9-49f6e4ef6314", "poster_offset": null }, { @@ -610,7 +610,7 @@ "rank": 2138535799, "id": "96024b01-28de-4ecd-9f7e-2dbb8dcb7dd4", "label": "P0413_B84_F04_009_11_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96024b01-28de-4ecd-9f7e-2dbb8dcb7dd4", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/96024b01-28de-4ecd-9f7e-2dbb8dcb7dd4", "poster_offset": null }, { @@ -620,7 +620,7 @@ "rank": 71582788, "id": "96259982-ac72-4ca8-bf54-cb2f5693965c", "label": "P0413_B84_F04_009_06_08.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96259982-ac72-4ca8-bf54-cb2f5693965c", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/96259982-ac72-4ca8-bf54-cb2f5693965c", "poster_offset": null }, { @@ -630,7 +630,7 @@ "rank": -1503238553, "id": "96ba2a1e-ee22-48ee-bb3e-cc8ed7e76486", "label": "P0413_B84_F04_009_02_08.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96ba2a1e-ee22-48ee-bb3e-cc8ed7e76486", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/96ba2a1e-ee22-48ee-bb3e-cc8ed7e76486", "poster_offset": null }, { @@ -640,7 +640,7 @@ "rank": -1145324612, "id": "96d4f624-e268-49ee-bf43-e8ddd4ff617f", "label": "P0413_B84_F04_009_03_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/96d4f624-e268-49ee-bf43-e8ddd4ff617f", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/96d4f624-e268-49ee-bf43-e8ddd4ff617f", "poster_offset": null }, { @@ -650,7 +650,7 @@ "rank": 1288490188, "id": "9ae9ad83-38b7-4454-94ea-6d1d8a10bea4", "label": "P0413_B84_F04_009_09_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9ae9ad83-38b7-4454-94ea-6d1d8a10bea4", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9ae9ad83-38b7-4454-94ea-6d1d8a10bea4", "poster_offset": null }, { @@ -660,7 +660,7 @@ "rank": 2075900859, "id": "9b0f27f5-e564-4fcb-b8ef-710cca78fc6d", "label": "P0413_B84_F04_009_11_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9b0f27f5-e564-4fcb-b8ef-710cca78fc6d", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9b0f27f5-e564-4fcb-b8ef-710cca78fc6d", "poster_offset": null }, { @@ -670,7 +670,7 @@ "rank": -572662306, "id": "9bd7f275-08f7-40c0-8dbb-9f62ed7a4534", "label": "P0413_B84_F04_009_05_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9bd7f275-08f7-40c0-8dbb-9f62ed7a4534", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9bd7f275-08f7-40c0-8dbb-9f62ed7a4534", "poster_offset": null }, { @@ -680,7 +680,7 @@ "rank": -286331153, "id": "9c3153bf-9d49-47f8-97ad-d4a69856e20b", "label": "P0413_B84_F04_009_06_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9c3153bf-9d49-47f8-97ad-d4a69856e20b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9c3153bf-9d49-47f8-97ad-d4a69856e20b", "poster_offset": null }, { @@ -690,7 +690,7 @@ "rank": 501079518, "id": "9ee3fe77-87e4-4ce5-86e8-a167f092e971", "label": "P0413_B84_F04_009_07_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9ee3fe77-87e4-4ce5-86e8-a167f092e971", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9ee3fe77-87e4-4ce5-86e8-a167f092e971", "poster_offset": null }, { @@ -700,7 +700,7 @@ "rank": -1717986918, "id": "9f1191b2-3c34-44c0-b33c-621a199f943b", "label": "P0413_B84_F04_009_02_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9f1191b2-3c34-44c0-b33c-621a199f943b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9f1191b2-3c34-44c0-b33c-621a199f943b", "poster_offset": null }, { @@ -710,7 +710,7 @@ "rank": -1360072977, "id": "9f984b13-0746-44a7-9e61-f8b5f8f8a5e8", "label": "P0413_B84_F04_009_02_10.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/9f984b13-0746-44a7-9e61-f8b5f8f8a5e8", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/9f984b13-0746-44a7-9e61-f8b5f8f8a5e8", "poster_offset": null }, { @@ -720,7 +720,7 @@ "rank": 787410671, "id": "a3517a51-b000-4b68-acb7-bb27eade4f61", "label": "P0413_B84_F04_009_08_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a3517a51-b000-4b68-acb7-bb27eade4f61", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/a3517a51-b000-4b68-acb7-bb27eade4f61", "poster_offset": null }, { @@ -730,7 +730,7 @@ "rank": -1002159036, "id": "a9676c51-43bb-4cbc-948f-3274d3d4ed5d", "label": "P0413_B84_F04_009_04_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a9676c51-43bb-4cbc-948f-3274d3d4ed5d", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/a9676c51-43bb-4cbc-948f-3274d3d4ed5d", "poster_offset": null }, { @@ -740,7 +740,7 @@ "rank": -1932735283, "id": "a9c8ef21-4482-4e12-961a-f2a41a0bb387", "label": "P0413_B84_F04_009_02_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a9c8ef21-4482-4e12-961a-f2a41a0bb387", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/a9c8ef21-4482-4e12-961a-f2a41a0bb387", "poster_offset": null }, { @@ -750,7 +750,7 @@ "rank": -2075900859, "id": "c6efe600-bc46-4b79-ab07-2a54014adc89", "label": "P0413_B84_F04_009_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89", "poster_offset": null }, { @@ -760,7 +760,7 @@ "rank": -1574821342, "id": "cc9e154e-bd38-4420-9a38-e17ccdcdce63", "label": "P0413_B84_F04_009_02_07.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/cc9e154e-bd38-4420-9a38-e17ccdcdce63", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/cc9e154e-bd38-4420-9a38-e17ccdcdce63", "poster_offset": null }, { @@ -770,7 +770,7 @@ "rank": -930576247, "id": "cd7f8d79-1f4d-4f43-b1de-0ef51a047ebd", "label": "P0413_B84_F04_009_04_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/cd7f8d79-1f4d-4f43-b1de-0ef51a047ebd", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/cd7f8d79-1f4d-4f43-b1de-0ef51a047ebd", "poster_offset": null }, { @@ -780,7 +780,7 @@ "rank": 1503238553, "id": "d2cc2196-c0d4-42a9-8582-c106ff5dd199", "label": "P0413_B84_F04_009_09_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/d2cc2196-c0d4-42a9-8582-c106ff5dd199", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/d2cc2196-c0d4-42a9-8582-c106ff5dd199", "poster_offset": null }, { @@ -790,7 +790,7 @@ "rank": -1073741824, "id": "e0c8628f-40b9-4cf1-bb60-df4a026e4e67", "label": "P0413_B84_F04_009_03_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e0c8628f-40b9-4cf1-bb60-df4a026e4e67", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/e0c8628f-40b9-4cf1-bb60-df4a026e4e67", "poster_offset": null }, { @@ -800,7 +800,7 @@ "rank": -214748365, "id": "e2af7752-dc31-4d00-ac89-02f0aeae7977", "label": "P0413_B84_F04_009_06_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e2af7752-dc31-4d00-ac89-02f0aeae7977", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/e2af7752-dc31-4d00-ac89-02f0aeae7977", "poster_offset": null }, { @@ -810,7 +810,7 @@ "rank": 1145324612, "id": "e8ce307a-7f3b-473d-892b-c53f87f28dc8", "label": "P0413_B84_F04_009_08_07.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e8ce307a-7f3b-473d-892b-c53f87f28dc8", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/e8ce307a-7f3b-473d-892b-c53f87f28dc8", "poster_offset": null }, { @@ -820,7 +820,7 @@ "rank": 1002159035, "id": "ed3290ea-36fe-48d6-a22b-b2919956ea4b", "label": "P0413_B84_F04_009_08_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ed3290ea-36fe-48d6-a22b-b2919956ea4b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/ed3290ea-36fe-48d6-a22b-b2919956ea4b", "poster_offset": null }, { @@ -830,7 +830,7 @@ "rank": 1932735283, "id": "f1090fcb-2d66-4aa7-8d78-86a0d659628b", "label": "P0413_B84_F04_009_10_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f1090fcb-2d66-4aa7-8d78-86a0d659628b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/f1090fcb-2d66-4aa7-8d78-86a0d659628b", "poster_offset": null }, { @@ -840,7 +840,7 @@ "rank": -858993459, "id": "f2e3be07-1e29-484b-8fae-b3075c7dbf05", "label": "P0413_B84_F04_009_05_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f2e3be07-1e29-484b-8fae-b3075c7dbf05", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/f2e3be07-1e29-484b-8fae-b3075c7dbf05", "poster_offset": null }, { @@ -850,7 +850,7 @@ "rank": -715827883, "id": "fb2e193d-c625-4bea-8aaa-67696187d28e", "label": "P0413_B84_F04_009_05_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fb2e193d-c625-4bea-8aaa-67696187d28e", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/fb2e193d-c625-4bea-8aaa-67696187d28e", "poster_offset": null }, { @@ -860,7 +860,7 @@ "rank": 644245094, "id": "fc9ff012-d5e2-49f6-aa8e-a713dee6ecb4", "label": "P0413_B84_F04_009_07_08.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fc9ff012-d5e2-49f6-aa8e-a713dee6ecb4", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/fc9ff012-d5e2-49f6-aa8e-a713dee6ecb4", "poster_offset": null }, { @@ -870,7 +870,7 @@ "rank": 1861152494, "id": "fee7fb28-57b5-4e39-ac24-ab1e6a5c8a9d", "label": "P0413_B84_F04_009_10_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fee7fb28-57b5-4e39-ac24-ab1e6a5c8a9d", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/fee7fb28-57b5-4e39-ac24-ab1e6a5c8a9d", "poster_offset": null }, { @@ -880,7 +880,7 @@ "rank": -1789569706, "id": "b098b7fc-f922-404d-8361-3b173c23fed5", "label": "P0413_B84_F04_009_02_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b098b7fc-f922-404d-8361-3b173c23fed5", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/b098b7fc-f922-404d-8361-3b173c23fed5", "poster_offset": null }, { @@ -890,7 +890,7 @@ "rank": -429496730, "id": "03abcb63-8886-4e06-bda9-2d0316537405", "label": "P0413_B84_F04_009_06_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/03abcb63-8886-4e06-bda9-2d0316537405", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/03abcb63-8886-4e06-bda9-2d0316537405", "poster_offset": null }, { @@ -900,7 +900,7 @@ "rank": -143165577, "id": "049050a9-e77d-4782-b97d-c6df3d7e0e45", "label": "P0413_B84_F04_009_06_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/049050a9-e77d-4782-b97d-c6df3d7e0e45", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/049050a9-e77d-4782-b97d-c6df3d7e0e45", "poster_offset": null }, { @@ -910,7 +910,7 @@ "rank": 715827882, "id": "0b514433-ce5e-4bf8-8e9c-f74f9bc666c7", "label": "P0413_B84_F04_009_08_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0b514433-ce5e-4bf8-8e9c-f74f9bc666c7", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/0b514433-ce5e-4bf8-8e9c-f74f9bc666c7", "poster_offset": null }, { @@ -920,7 +920,7 @@ "rank": -644245094, "id": "0e691e0b-85e0-4c64-b47f-8dc8aa65249c", "label": "P0413_B84_F04_009_05_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0e691e0b-85e0-4c64-b47f-8dc8aa65249c", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/0e691e0b-85e0-4c64-b47f-8dc8aa65249c", "poster_offset": null }, { @@ -930,7 +930,7 @@ "rank": -1288490189, "id": "0eadcacd-43cc-4789-a751-549e6d4161dd", "label": "P0413_B84_F04_009_03_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/0eadcacd-43cc-4789-a751-549e6d4161dd", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/0eadcacd-43cc-4789-a751-549e6d4161dd", "poster_offset": null }, { @@ -940,7 +940,7 @@ "rank": 858993459, "id": "12e1a575-7578-4215-a786-a6716f08117c", "label": "P0413_B84_F04_009_08_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/12e1a575-7578-4215-a786-a6716f08117c", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/12e1a575-7578-4215-a786-a6716f08117c", "poster_offset": null }, { @@ -950,7 +950,7 @@ "rank": 1717986918, "id": "179ba4ce-2b21-4842-bc25-b96a4492be50", "label": "P0413_B84_F04_009_10_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/179ba4ce-2b21-4842-bc25-b96a4492be50", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/179ba4ce-2b21-4842-bc25-b96a4492be50", "poster_offset": null }, { @@ -960,7 +960,7 @@ "rank": -357913941, "id": "198c0b2a-1fb6-4488-b448-96c065aad25e", "label": "P0413_B84_F04_009_06_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/198c0b2a-1fb6-4488-b448-96c065aad25e", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/198c0b2a-1fb6-4488-b448-96c065aad25e", "poster_offset": null }, { @@ -970,7 +970,7 @@ "rank": 930576247, "id": "1fd583bb-f1d5-481b-ab29-97b21425560b", "label": "P0413_B84_F04_009_08_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/1fd583bb-f1d5-481b-ab29-97b21425560b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/1fd583bb-f1d5-481b-ab29-97b21425560b", "poster_offset": null }, { @@ -980,7 +980,7 @@ "rank": 1431655765, "id": "20739142-0e37-420e-81b4-e6d9fcbdf6d6", "label": "P0413_B84_F04_009_09_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/20739142-0e37-420e-81b4-e6d9fcbdf6d6", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/20739142-0e37-420e-81b4-e6d9fcbdf6d6", "poster_offset": null }, { @@ -990,7 +990,7 @@ "rank": 2111692253, "id": "27b1e8ad-3453-40ce-bc95-4513a74dabfe", "label": "P0413_B84_F04_009_11_02.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/27b1e8ad-3453-40ce-bc95-4513a74dabfe", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/27b1e8ad-3453-40ce-bc95-4513a74dabfe", "poster_offset": null }, { @@ -1000,7 +1000,7 @@ "rank": 143165576, "id": "2b41a266-cd5c-4b9f-ba8c-c9731077ab84", "label": "P0413_B84_F04_009_07_01.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2b41a266-cd5c-4b9f-ba8c-c9731077ab84", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/2b41a266-cd5c-4b9f-ba8c-c9731077ab84", "poster_offset": null }, { @@ -1010,7 +1010,7 @@ "rank": -501079518, "id": "2df2e469-2a45-44d1-9d74-5b9324df8b14", "label": "P0413_B84_F04_009_05_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2df2e469-2a45-44d1-9d74-5b9324df8b14", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/2df2e469-2a45-44d1-9d74-5b9324df8b14", "poster_offset": null }, { @@ -1020,7 +1020,7 @@ "rank": 2129587950, "id": "2ec83ff3-56b4-4105-ac36-8da8b4dd23a6", "label": "P0413_B84_F04_009_11_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/2ec83ff3-56b4-4105-ac36-8da8b4dd23a6", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/2ec83ff3-56b4-4105-ac36-8da8b4dd23a6", "poster_offset": null }, { @@ -1030,7 +1030,7 @@ "rank": 429496729, "id": "46cd43fe-5e01-4bec-9539-c38367a1e906", "label": "P0413_B84_F04_009_07_05.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/46cd43fe-5e01-4bec-9539-c38367a1e906", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/46cd43fe-5e01-4bec-9539-c38367a1e906", "poster_offset": null }, { @@ -1040,7 +1040,7 @@ "rank": -1861152495, "id": "4dd19cf4-8224-490d-8a67-3e72a183cb26", "label": "P0413_B84_F04_009_02_03.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/4dd19cf4-8224-490d-8a67-3e72a183cb26", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/4dd19cf4-8224-490d-8a67-3e72a183cb26", "poster_offset": null }, { @@ -1050,7 +1050,7 @@ "rank": 357913941, "id": "56c1d785-7144-496d-8b07-c6366161fbe6", "label": "P0413_B84_F04_009_07_04.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/56c1d785-7144-496d-8b07-c6366161fbe6", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/56c1d785-7144-496d-8b07-c6366161fbe6", "poster_offset": null }, { @@ -1060,7 +1060,7 @@ "rank": -71582788, "id": "59facd76-ac09-4eba-a558-0580ed1500f5", "label": "P0413_B84_F04_009_06_06.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/59facd76-ac09-4eba-a558-0580ed1500f5", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/59facd76-ac09-4eba-a558-0580ed1500f5", "poster_offset": null } ], @@ -1079,7 +1079,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "c6efe600-bc46-4b79-ab07-2a54014adc89", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/c6efe600-bc46-4b79-ab07-2a54014adc89" }, "api_model": "Work", "physical_description_material": [], @@ -1107,9 +1107,9 @@ "create_date": "2022-06-27T21:14:38.058837Z", "identifier": [], "box_number": [], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e/full/!300,300/0/default.jpg", "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ae/1f/46/c8/-4/66/c-/49/d8/-8/cf/9-/af/58/69/69/c4/ce-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/ae/1f/46/c8/-4/66/c-/49/d8/-8/cf/9-/af/58/69/69/c4/ce-manifest.json", "work_type": "Image", "collection": {}, "published": false, @@ -1126,7 +1126,7 @@ "rank": 0, "id": "815972ba-ad41-4275-9897-77563412e82e", "label": "label", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e", "poster_offset": null } ], @@ -1145,7 +1145,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "815972ba-ad41-4275-9897-77563412e82e", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/815972ba-ad41-4275-9897-77563412e82e" }, "api_model": "Work", "physical_description_material": [], @@ -1173,9 +1173,9 @@ "create_date": "2022-06-27T21:13:53.700541Z", "identifier": [], "box_number": [], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e/full/!300,300/0/default.jpg", "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/d5/c7/38/43/-5/50/d-/47/aa/-8/ab/b-/cd/80/13/ed/32/af-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/d5/c7/38/43/-5/50/d-/47/aa/-8/ab/b-/cd/80/13/ed/32/af-manifest.json", "work_type": "Image", "collection": {}, "published": false, @@ -1202,7 +1202,7 @@ "rank": 0, "id": "01c3b28f-df15-4055-a947-b0f192e94d8e", "label": "label", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e", "poster_offset": null } ], @@ -1221,7 +1221,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "01c3b28f-df15-4055-a947-b0f192e94d8e", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/01c3b28f-df15-4055-a947-b0f192e94d8e" }, "api_model": "Work", "physical_description_material": [], @@ -1249,9 +1249,9 @@ "create_date": "2022-06-27T21:15:38.960884Z", "identifier": [], "box_number": [], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b/full/!300,300/0/default.jpg", "visibility": "Private", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/ed/9c/79/67/-6/2a/b-/41/6c/-a/3a/f-/49/2c/e8/65/54/9f-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/ed/9c/79/67/-6/2a/b-/41/6c/-a/3a/f-/49/2c/e8/65/54/9f-manifest.json", "work_type": "Image", "collection": {}, "published": false, @@ -1268,7 +1268,7 @@ "rank": 0, "id": "e17dd686-0749-4466-8983-27b5b62ff27b", "label": "label", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b", "poster_offset": null }, { @@ -1297,7 +1297,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "e17dd686-0749-4466-8983-27b5b62ff27b", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/e17dd686-0749-4466-8983-27b5b62ff27b" }, "api_model": "Work", "physical_description_material": [], @@ -1336,9 +1336,9 @@ "create_date": "2021-03-16T05:58:30.479386Z", "identifier": ["MS 63"], "box_number": ["3"], - "thumbnail": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44/full/!300,300/0/default.jpg", + "thumbnail": "https://assets.test.library.northwestern.edu.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44/full/!300,300/0/default.jpg", "visibility": "Public", - "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/8d/71/0f/86/-e/c5/4-/43/cf/-9/ec/4-/eb/71/b1/c9/0c/5d-manifest.json", + "iiif_manifest": "https://assets.test.library.northwestern.edu.edu/public/8d/71/0f/86/-e/c5/4-/43/cf/-9/ec/4-/eb/71/b1/c9/0c/5d-manifest.json", "work_type": "Image", "collection": { "id": "18ec4c6b-192a-4ab8-9903-ea0f393c35f7", @@ -1368,7 +1368,7 @@ "rank": 0, "id": "ac987e48-b6a5-4d34-b5e0-1d2006996a44", "label": "BFMF_B03_F19_005_001n_am.tif", - "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44", + "representative_image_url": "https://assets.test.library.northwestern.edu.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44", "poster_offset": null } ], @@ -1393,7 +1393,7 @@ "abstract": [], "representative_file_set": { "fileSetId": "ac987e48-b6a5-4d34-b5e0-1d2006996a44", - "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44" + "url": "https://assets.test.library.northwestern.edu.edu/iiif/2/ac987e48-b6a5-4d34-b5e0-1d2006996a44" }, "api_model": "Work", "physical_description_material": [], diff --git a/tests/test-helpers.js b/tests/test-helpers.js index 8df51bea..157011f7 100644 --- a/tests/test-helpers.js +++ b/tests/test-helpers.js @@ -12,10 +12,11 @@ global.helpers = { }, mockIndex: () => { - const mock = nock("https://index.library.northwestern.edu"); + const mock = nock("https://index.test.library.northwestern.edu"); beforeEach(function () { - process.env.ELASTICSEARCH_ENDPOINT = "index.library.northwestern.edu"; + process.env.ELASTICSEARCH_ENDPOINT = + "index.test.library.northwestern.edu"; }); afterEach(function () { diff --git a/tests/unit/api/opensearch.test.js b/tests/unit/api/opensearch.test.js index 30d9f52a..f9b1f7ee 100644 --- a/tests/unit/api/opensearch.test.js +++ b/tests/unit/api/opensearch.test.js @@ -27,7 +27,7 @@ describe("getWork()", function () { const result = await opensearch.getWork("1234"); const body = JSON.parse(result.body); expect(result.statusCode).to.eq(404); - expect(body.found).to.eq(false); + expect(body.found).to.be.false; }); it("returns 404 Not Found for missing documents", async function () { @@ -38,7 +38,7 @@ describe("getWork()", function () { const result = await opensearch.getWork("1234"); const body = JSON.parse(result.body); expect(result.statusCode).to.eq(404); - expect(body.found).to.eq(false); + expect(body.found).to.be.false; }); }); diff --git a/tests/unit/api/request/models.test.js b/tests/unit/api/request/models.test.js new file mode 100644 index 00000000..59d9774b --- /dev/null +++ b/tests/unit/api/request/models.test.js @@ -0,0 +1,29 @@ +"use strict"; + +const models = require("../../../../src/api/request/models"); +const chai = require("chai"); +const expect = chai.expect; + +describe("models", () => { + helpers.saveEnvironment(); + + it("knows valid models", () => { + expect(models.validModels(["collections", "file-sets", "works"])).to.be + .true; + }); + + it("detects invalid models", () => { + expect(models.validModels(["works", "foo"])).to.be.false; + }); + + it("maps models to targets", () => { + let result = models.modelsToTargets(["collections", "file-sets", "works"]); + expect(result).to.eq("dc-v2-collection,dc-v2-file-set,dc-v2-work"); + + process.env.ENV_PREFIX = "pre"; + result = models.modelsToTargets(["collections", "file-sets", "works"]); + expect(result).to.eq( + "pre-dc-v2-collection,pre-dc-v2-file-set,pre-dc-v2-work" + ); + }); +}); diff --git a/tests/unit/api/request/pipeline.test.js b/tests/unit/api/request/pipeline.test.js new file mode 100644 index 00000000..a038fe12 --- /dev/null +++ b/tests/unit/api/request/pipeline.test.js @@ -0,0 +1,42 @@ +"use strict"; + +const RequestPipeline = require("../../../../src/api/request/pipeline"); +const chai = require("chai"); +const expect = chai.expect; + +describe("RequestPipeline", () => { + const requestBody = { + query: { match: { term: { title: "The Title" } } }, + size: 50, + sort: [{ create_date: "asc" }], + _source: ["id", "title", "collection"], + aggs: { collection: { terms: { field: "contributor.label", size: 10 } } }, + }; + + let pipeline; + beforeEach(() => { + pipeline = new RequestPipeline(requestBody); + }); + + it("adds an auth filter", () => { + const result = pipeline.authFilter(); + expect(result.searchContext.size).to.eq(50); + expect(result.searchContext.query.bool.must).to.include(requestBody.query); + expect(result.searchContext.query.bool.must_not).to.deep.include( + { term: { visibility: "Private" } }, + { term: { published: false } } + ); + }); + + it("sets a default query", () => { + const result = new RequestPipeline({ size: 20 }).authFilter(); + expect(result.searchContext.size).to.eq(20); + expect(result.searchContext.query.bool.must).to.deep.include({ + match_all: {}, + }); + }); + + it("serializes JSON", () => { + expect(JSON.parse(pipeline.toJson())).to.deep.equal(requestBody); + }); +}); diff --git a/tests/unit/api/response/opensearch.test.js b/tests/unit/api/response/opensearch.test.js new file mode 100644 index 00000000..41a492e5 --- /dev/null +++ b/tests/unit/api/response/opensearch.test.js @@ -0,0 +1,45 @@ +"use strict"; + +const transformer = require("../../../../src/api/response/opensearch"); +const chai = require("chai"); +const expect = chai.expect; + +describe("OpenSearch response transformer", () => { + it("transforms a doc response", async () => { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/work-1234.json"), + }; + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(200); + + const body = JSON.parse(result.body); + expect(body.data).to.be.an("object"); + }); + + it("transforms a search response", async () => { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/search.json"), + }; + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(200); + + const body = JSON.parse(result.body); + expect(body.data).to.be.an("array"); + }); + + it("transforms an error response", async () => { + const response = { + statusCode: 404, + body: helpers.testFixture("mocks/missing-index.json"), + }; + + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(404); + + const body = JSON.parse(result.body); + expect(body.status).to.eq(404); + expect(body.error).to.be.a("string"); + }); +}); diff --git a/tests/unit/aws/environment.test.js b/tests/unit/aws/environment.test.js index aba3b717..8076a6de 100644 --- a/tests/unit/aws/environment.test.js +++ b/tests/unit/aws/environment.test.js @@ -9,9 +9,9 @@ describe("environment", function () { helpers.saveEnvironment(); it("returns the index endpoint", function () { - process.env.ELASTICSEARCH_ENDPOINT = "index.library.northwestern.edu"; + process.env.ELASTICSEARCH_ENDPOINT = "index.test.library.northwestern.edu"; expect(environment.elasticsearchEndpoint()).to.eq( - "index.library.northwestern.edu" + "index.test.library.northwestern.edu" ); }); From 7b1e4809af51e6c905430e666c9f2097b1ba3d87 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 15 Aug 2022 16:10:41 +0000 Subject: [PATCH 08/61] Add pagination to search --- package-lock.json | 16 +++- package.json | 3 +- src/api/pagination.js | 84 +++++++++++++++++ src/api/response/opensearch/index.js | 19 ++-- src/handlers/search.js | 80 ++++++++++------- src/helpers.js | 23 +++++ template.yaml | 33 ++++++- tests/unit/api/helpers.test.js | 100 +++++++++++++++++++++ tests/unit/api/pagination.test.js | 74 +++++++++++++++ tests/unit/api/response/opensearch.test.js | 27 +++++- 10 files changed, 411 insertions(+), 48 deletions(-) create mode 100644 src/api/pagination.js create mode 100644 src/helpers.js create mode 100644 tests/unit/api/helpers.test.js create mode 100644 tests/unit/api/pagination.test.js diff --git a/package-lock.json b/package-lock.json index 87682012..d2a70e87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "@aws-sdk/node-http-handler": "^3.127.0", "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", - "axios": ">=0.21.1" + "axios": ">=0.21.1", + "lz-string": "^1.4.4" }, "devDependencies": { "chai": "^4.2.0", @@ -2258,6 +2259,14 @@ "get-func-name": "^2.0.0" } }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "3.1.0", "dev": true, @@ -4659,6 +4668,11 @@ "get-func-name": "^2.0.0" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" + }, "make-dir": { "version": "3.1.0", "dev": true, diff --git a/package.json b/package.json index 11dbe541..7706660a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "@aws-sdk/node-http-handler": "^3.127.0", "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", - "axios": ">=0.21.1" + "axios": ">=0.21.1", + "lz-string": "^1.4.4" }, "scripts": { "prettier": "prettier -c src tests", diff --git a/src/api/pagination.js b/src/api/pagination.js new file mode 100644 index 00000000..09b44872 --- /dev/null +++ b/src/api/pagination.js @@ -0,0 +1,84 @@ +const { + decompressFromEncodedURIComponent: decompress, + compressToEncodedURIComponent: compress, +} = require("lz-string"); + +const encodeFields = ["query", "size", "sort", "fields", "_source"]; + +async function decodeSearchToken(token) { + return JSON.parse(await decompress(token)); +} + +async function encodeSearchToken(models, body) { + let token = { body: { size: 10 }, models }; + for (const field in body) { + if (encodeFields.includes(field)) { + token.body[field] = body[field]; + } + } + return await compress(JSON.stringify(token)); +} + +function from(body) { + return body?.from || 0; +} +function size(body) { + return body?.size || 10; +} + +function maxPage(body, count) { + return Math.ceil(count / size(body)); +} + +function nextPage(body, count) { + const current = thisPage(body); + return maxPage(body, count) > current ? current + 1 : null; +} + +function prevPage(body, _count) { + return body.from > 0 ? thisPage(body) - 1 : null; +} + +function thisPage(body) { + return Math.floor(from(body) / size(body) + 1); +} + +class Paginator { + constructor(baseUrl, models, body) { + this.baseUrl = baseUrl; + this.models = models; + this.body = { ...body }; + } + + async pageInfo(count) { + let url = new URL("search", this.baseUrl); + url.searchParams.set( + "searchToken", + await encodeSearchToken(this.models, this.body) + ); + + const prev = prevPage(this.body, count); + const next = nextPage(this.body, count); + + let result = { + query_url: url.toString(), + current_page: thisPage(this.body), + limit: size(this.body), + offset: from(this.body), + total_hits: count, + total_pages: maxPage(this.body, count), + }; + if (prev) { + url.searchParams.set("page", prev); + result.prev_url = url.toString(); + } + if (next) { + url.searchParams.set("page", next); + result.next_url = url.toString(); + } + + return result; + } +} + +module.exports = { decodeSearchToken, encodeSearchToken, Paginator }; diff --git a/src/api/response/opensearch/index.js b/src/api/response/opensearch/index.js index 1d72759c..c1222c28 100644 --- a/src/api/response/opensearch/index.js +++ b/src/api/response/opensearch/index.js @@ -1,26 +1,26 @@ -function transform(response) { +async function transform(response, pager) { if (response.statusCode === 200) { const responseBody = JSON.parse(response.body); - return responseBody?.hits?.hits - ? transformMany(responseBody) - : transformOne(responseBody); + return await (responseBody?.hits?.hits + ? transformMany(responseBody, pager) + : transformOne(responseBody)); } return transformError(response); } -function transformOne(responseBody) { +async function transformOne(responseBody) { return { statusCode: 200, body: JSON.stringify({ data: responseBody._source, info: {} }), }; } -function transformMany(responseBody) { +async function transformMany(responseBody, pager) { return { statusCode: 200, body: JSON.stringify({ data: extractSource(responseBody.hits.hits), - pagination: { total: responseBody.hits.total.value }, + pagination: await pager.pageInfo(responseBody.hits.total.value), info: {}, aggregations: responseBody.aggregations, }), @@ -28,7 +28,10 @@ function transformMany(responseBody) { } function transformError(response) { - const responseBody = { status: response.statusCode, error: "TODO" }; + const responseBody = { + status: response.statusCode, + error: "TODO", + }; return { statusCode: response.statusCode, diff --git a/src/handlers/search.js b/src/handlers/search.js index 8097bda8..590fcada 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,49 +1,63 @@ +const { baseUrl } = require("../helpers"); const { modelsToTargets, validModels } = require("../api/request/models"); const { search } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); -const RequestPipeline = require("../api/request/pipeline.js"); +const { decodeSearchToken, Paginator } = require("../api/pagination"); +const RequestPipeline = require("../api/request/pipeline"); -/** - * Function to wrap search requests and transform responses - */ -exports.handler = async (event) => { - const eventBody = event.body; +const getSearch = async (event) => { + let token = event.queryStringParameters.searchToken; + if (token === undefined || token === "") { + return invalidRequest("searchToken parameter is required"); + } + + let request; + try { + request = await decodeSearchToken(token); + } catch (err) { + return invalidRequest("searchToken is invalid"); + } + + const page = Number(event.queryStringParameters.page || 1); + request.body.from = request.body.size * (page - 1); + + return await executeSearch(event, request.models, request.body); +}; + +const postSearch = async (event) => { + const eventBody = JSON.parse(event.body); const requestedModels = event.pathParameters?.models == null ? ["works"] : event.pathParameters.models.split(","); - if (!validModels(requestedModels)) { - return { - statusCode: 400, - body: JSON.stringify({ - message: `Invalid models requested: ${requestedModels}`, - }), - }; + return await executeSearch(event, requestedModels, eventBody); +}; + +/** + * Function to wrap search requests and transform responses + */ +const executeSearch = async (event, models, body) => { + if (!validModels(models)) { + return invalidRequest(`Invalid models requested: ${models}`); } - const filteredBody = new RequestPipeline(eventBody).authFilter().toJson(); - let esResponse = await search(modelsToTargets(requestedModels), filteredBody); - let transformedResponse = opensearchResponse.transform(esResponse); + const pager = new Paginator(baseUrl(event), models, body); + const filteredBody = new RequestPipeline(body).authFilter().toJson(); + let esResponse = await search(modelsToTargets(models), filteredBody); + let transformedResponse = await opensearchResponse.transform( + esResponse, + pager + ); return transformedResponse; }; -function validModels(models) { - const validModels = models.filter((model) => isAllowed(model)); - return validModels.length > 0; -} - -function isAllowed(model) { - allowedModels = ["works", "file-sets", "collections"]; - return allowedModels.includes(model); -} - -function modelsToTargets(models) { - const mapTargets = { - works: "dc-v2-work", - "file-sets": "dc-v2-file-set", - collections: "dc-v2-collection", +const invalidRequest = (message) => { + return { + statusCode: 400, + body: JSON.stringify({ message: message }), }; - return String(models.map((model) => prefix(mapTargets[model]))); -} +}; + +module.exports = { postSearch, getSearch }; diff --git a/src/helpers.js b/src/helpers.js new file mode 100644 index 00000000..28f354fe --- /dev/null +++ b/src/helpers.js @@ -0,0 +1,23 @@ +const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; + +function baseUrl(event) { + const scheme = event.headers["X-Forwarded-Proto"]; + + // The localhost check only matters in dev mode, but it's + // really inconvenient not to have it + const host = + event.requestContext.domainName === "localhost" + ? event.headers["Host"].split(/:/)[0] + : event.requestContext.domainName; + const port = event.headers["X-Forwarded-Port"]; + + let result = new URL(`${scheme}://${host}:${port}`); + const stage = event.requestContext?.stage; + + if (gatewayRe.test(result.host) && stage !== "$default") { + result = new URL(`${stage}/`, result); + } + return result.toString(); +} + +module.exports = { baseUrl }; diff --git a/template.yaml b/template.yaml index 48b89cb1..1c884929 100644 --- a/template.yaml +++ b/template.yaml @@ -106,10 +106,10 @@ Resources: ApiId: !Ref dcApi Path: /works/{id} Method: GET - searchFunction: + searchPostFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/search.handler + Handler: src/handlers/search.postSearch Runtime: nodejs16.x Architectures: - x86_64 @@ -141,6 +141,35 @@ Resources: ApiId: !Ref dcApi Path: /search/{models} Method: POST + searchGetFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/search.getSearch + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Handles paging requests + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + ENV_PREFIX: !Ref EnvironmentPrefix + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + Events: + SearchApi: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /search + Method: GET dcApi: Type: AWS::Serverless::HttpApi Properties: diff --git a/tests/unit/api/helpers.test.js b/tests/unit/api/helpers.test.js new file mode 100644 index 00000000..4bb8b230 --- /dev/null +++ b/tests/unit/api/helpers.test.js @@ -0,0 +1,100 @@ +"use strict"; + +const { baseUrl } = require("../../../src/helpers"); +const chai = require("chai"); +const expect = chai.expect; + +describe("helpers", () => { + describe("baseUrl()", () => { + it("extracts the base URL from a local event", () => { + const event = { + headers: { + Host: "localhost", + "X-Forwarded-Proto": "http", + "X-Forwarded-Port": "3000", + }, + requestContext: { + domainName: "localhost", + domainPrefix: "localhost", + stage: "v2", + }, + }; + + expect(baseUrl(event)).to.eq("http://localhost:3000/"); + }); + + it("extracts the base URL from an API Gateway event", () => { + const event = { + headers: { + Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", + "X-Forwarded-Proto": "https", + "X-Forwarded-Port": "443", + }, + requestContext: { + domainName: "abcdefghijz.execute-api.us-east-1.amazonaws.com", + domainPrefix: "abcdefghijz", + stage: "v2", + }, + }; + + expect(baseUrl(event)).to.eq( + "https://abcdefghijz.execute-api.us-east-1.amazonaws.com/v2/" + ); + }); + + it("extracts the base URL from a CloudWatch event", () => { + const event = { + headers: { + Host: "abcdefghijz.cloudfront.net", + "X-Forwarded-Proto": "https", + "X-Forwarded-Port": "443", + }, + requestContext: { + domainName: "abcdefghijz.cloudfront.net", + domainPrefix: "abcdefghijz", + stage: "v2", + }, + }; + + expect(baseUrl(event)).to.eq("https://abcdefghijz.cloudfront.net/"); + }); + + it("extracts the base URL from an event with a custom domain", () => { + const event = { + headers: { + Host: "api.test.library.northwestern.edu", + "X-Forwarded-Proto": "https", + "X-Forwarded-Port": "443", + }, + requestContext: { + domainName: "api.test.library.northwestern.edu", + domainPrefix: "api", + stage: "v2", + }, + }; + + expect(baseUrl(event)).to.eq( + "https://api.test.library.northwestern.edu/" + ); + }); + + it("prefers a custom domain over localhost", () => { + const event = { + headers: { + Host: "localhost", + "X-Forwarded-Proto": "http", + "X-Forwarded-Port": "3000", + }, + requestContext: { + domainName: "api.test.library.northwestern.edu", + domainPrefix: "api", + stage: "v2", + }, + }; + + expect(baseUrl(event)).to.eq( + "http://api.test.library.northwestern.edu:3000/" + ); + }); + }); +}); diff --git a/tests/unit/api/pagination.test.js b/tests/unit/api/pagination.test.js new file mode 100644 index 00000000..1a7bb582 --- /dev/null +++ b/tests/unit/api/pagination.test.js @@ -0,0 +1,74 @@ +"use strict"; + +const { decodeSearchToken, Paginator } = require("../../../src/api/pagination"); +const chai = require("chai"); +const expect = chai.expect; + +describe("Paginator", function () { + const requestBody = { + query: { match_all: {} }, + size: 50, + sort: [{ create_date: "asc" }], + _source: ["id", "title", "collection"], + aggs: { collection: { terms: { field: "contributor.label", size: 10 } } }, + }; + + let pager; + + this.beforeEach(() => { + pager = new Paginator( + "http://dcapi.library.northwestern.edu/v2/", + ["works"], + requestBody + ); + }); + + it("produces page 1 pagination", async () => { + let result = await pager.pageInfo(1275); + expect(result.current_page).to.eq(1); + expect(result.offset).to.eq(0); + expect(result.limit).to.eq(50); + expect(result.total_hits).to.eq(1275); + expect(result.total_pages).to.eq(26); + expect(result.prev_url).to.be.undefined; + + let url = new URL(result.next_url); + expect(url.host).to.eq("dcapi.library.northwestern.edu"); + expect(url.pathname).to.eq("/v2/search"); + expect(url.searchParams.get("page")).to.eq("2"); + }); + + it("produces additional page pagination", async () => { + pager.body.from = 100; + let result = await pager.pageInfo(1275); + expect(result.current_page).to.eq(3); + expect(new URL(result.prev_url).searchParams.get("page")).to.eq("2"); + expect(new URL(result.next_url).searchParams.get("page")).to.eq("4"); + expect(requestBody.from).to.be.undefined; + }); + + it("produces last page pagination", async () => { + pager.body.from = 1270; + const result = await pager.pageInfo(1275); + expect(result.next_url).to.be.undefined; + }); + + it("produces a usable token", async () => { + pager.body.from = 100; + const result = await pager.pageInfo(1275); + const token = new URL(result.query_url).searchParams.get("searchToken"); + const rehydrated = await decodeSearchToken(token); + + expect(rehydrated.models).to.include.members(["works"]); + for (const field of ["query", "size", "sort", "_source"]) { + expect(rehydrated.body[field]).to.deep.equal(requestBody[field]); + } + expect(rehydrated.body).not.to.include.keys(["aggs", "from"]); + }); + + it("correctly sets the default size", async () => { + delete pager.body.size; + const result = await pager.pageInfo(1275); + expect(result.limit).to.eq(10); + }); +}); diff --git a/tests/unit/api/response/opensearch.test.js b/tests/unit/api/response/opensearch.test.js index 41a492e5..6df5e7d0 100644 --- a/tests/unit/api/response/opensearch.test.js +++ b/tests/unit/api/response/opensearch.test.js @@ -1,20 +1,31 @@ "use strict"; const transformer = require("../../../../src/api/response/opensearch"); +const { Paginator } = require("../../../../src/api/pagination"); const chai = require("chai"); const expect = chai.expect; describe("OpenSearch response transformer", () => { + let pager; + beforeEach(() => { + pager = new Paginator( + "http://dcapi.library.northwestern.edu/v2/", + ["works"], + { query: { match_all: {} } } + ); + }); + it("transforms a doc response", async () => { const response = { statusCode: 200, body: helpers.testFixture("mocks/work-1234.json"), }; - const result = await transformer.transform(response); + const result = await transformer.transform(response, pager); expect(result.statusCode).to.eq(200); const body = JSON.parse(result.body); expect(body.data).to.be.an("object"); + expect(body).not.to.include.key("pagination"); }); it("transforms a search response", async () => { @@ -22,11 +33,21 @@ describe("OpenSearch response transformer", () => { statusCode: 200, body: helpers.testFixture("mocks/search.json"), }; - const result = await transformer.transform(response); + const result = await transformer.transform(response, pager); expect(result.statusCode).to.eq(200); const body = JSON.parse(result.body); expect(body.data).to.be.an("array"); + expect(body).to.include.key("pagination"); + expect(body.pagination).to.include.keys([ + "query_url", + "current_page", + "limit", + "next_url", + "offset", + "total_hits", + "total_pages", + ]); }); it("transforms an error response", async () => { @@ -35,7 +56,7 @@ describe("OpenSearch response transformer", () => { body: helpers.testFixture("mocks/missing-index.json"), }; - const result = await transformer.transform(response); + const result = await transformer.transform(response, pager); expect(result.statusCode).to.eq(404); const body = JSON.parse(result.body); From 9df7f99e14768f544b51c40dde0e649d3463711b Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 16 Aug 2022 02:07:31 +0000 Subject: [PATCH 09/61] Add integration tests and improve mocha config --- .mocharc.js | 5 + .npmignore | 2 +- nyc.config.js | 2 +- package-lock.json | 25 ++++ package.json | 7 +- src/api/opensearch.js | 3 + src/handlers/middleware.js | 10 ++ src/handlers/search.js | 2 + src/helpers.js | 12 +- .../fixtures/mocks/collection-1234.json | 0 .../fixtures/mocks/fileset-1234.json | 0 .../fixtures/mocks/missing-index.json | 0 .../fixtures/mocks/missing-work-1234.json | 0 .../mocks/search-multiple-targets.json | 0 {tests => test}/fixtures/mocks/search.json | 0 .../fixtures/mocks/unpublished-work-1234.json | 0 {tests => test}/fixtures/mocks/work-1234.json | 0 test/integration/get-doc.test.js | 93 ++++++++++++++ test/integration/search.test.js | 113 ++++++++++++++++++ test/test-helpers/event-builder.js | 83 +++++++++++++ .../test-helpers/index.js | 7 +- {tests => test}/unit/api/helpers.test.js | 22 +++- {tests => test}/unit/api/opensearch.test.js | 0 {tests => test}/unit/api/pagination.test.js | 0 .../unit/api/request/models.test.js | 0 .../unit/api/request/pipeline.test.js | 0 .../unit/api/response/opensearch.test.js | 0 {tests => test}/unit/aws/environment.test.js | 0 28 files changed, 375 insertions(+), 11 deletions(-) create mode 100644 .mocharc.js create mode 100644 src/handlers/middleware.js rename {tests => test}/fixtures/mocks/collection-1234.json (100%) rename {tests => test}/fixtures/mocks/fileset-1234.json (100%) rename {tests => test}/fixtures/mocks/missing-index.json (100%) rename {tests => test}/fixtures/mocks/missing-work-1234.json (100%) rename {tests => test}/fixtures/mocks/search-multiple-targets.json (100%) rename {tests => test}/fixtures/mocks/search.json (100%) rename {tests => test}/fixtures/mocks/unpublished-work-1234.json (100%) rename {tests => test}/fixtures/mocks/work-1234.json (100%) create mode 100644 test/integration/get-doc.test.js create mode 100644 test/integration/search.test.js create mode 100644 test/test-helpers/event-builder.js rename tests/test-helpers.js => test/test-helpers/index.js (76%) rename {tests => test}/unit/api/helpers.test.js (79%) rename {tests => test}/unit/api/opensearch.test.js (100%) rename {tests => test}/unit/api/pagination.test.js (100%) rename {tests => test}/unit/api/request/models.test.js (100%) rename {tests => test}/unit/api/request/pipeline.test.js (100%) rename {tests => test}/unit/api/response/opensearch.test.js (100%) rename {tests => test}/unit/aws/environment.test.js (100%) diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 00000000..b07081b6 --- /dev/null +++ b/.mocharc.js @@ -0,0 +1,5 @@ +module.exports = { + ignore: ["test/test-helpers/**/*"], + recursive: true, + require: "test/test-helpers" +} \ No newline at end of file diff --git a/.npmignore b/.npmignore index e7e1fb04..ab1cfb4e 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1 @@ -tests/* +test/* diff --git a/nyc.config.js b/nyc.config.js index 1e8f0209..bff6f96b 100644 --- a/nyc.config.js +++ b/nyc.config.js @@ -1,7 +1,7 @@ 'use strict'; const defaultExclude = require('@istanbuljs/schema/default-exclude'); -const localExclude = ["src/handlers/**"]; +const localExclude = [".aws-sam/**/*"]; module.exports = { all: true, "check-coverage": true, diff --git a/package-lock.json b/package-lock.json index d2a70e87..7429571e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", + "base64-js": "^1.5.1", "lz-string": "^1.4.4" }, "devDependencies": { @@ -1325,6 +1326,25 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "dev": true, @@ -4122,6 +4142,11 @@ "version": "1.0.2", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "binary-extensions": { "version": "2.2.0", "dev": true diff --git a/package.json b/package.json index 7706660a..8549ad8b 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,13 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", + "base64-js": "^1.5.1", "lz-string": "^1.4.4" }, "scripts": { - "prettier": "prettier -c src tests", - "prettier:fix": "prettier -cw src tests", - "test": "mocha --require tests/test-helpers --recursive tests/unit", + "prettier": "prettier -c src test", + "prettier:fix": "prettier -cw src test", + "test": "mocha", "test:coverage": "nyc npm test" }, "devDependencies": { diff --git a/src/api/opensearch.js b/src/api/opensearch.js index d72fb139..d22816ff 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -37,6 +37,9 @@ async function getDocument(index, id) { } function isVisible(doc) { + if (!doc?.found) { + return false; + } if (doc?._source.api_model == "FileSet") { return doc?._source?.visibility !== "Private"; } else { diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js new file mode 100644 index 00000000..fb8e624f --- /dev/null +++ b/src/handlers/middleware.js @@ -0,0 +1,10 @@ +const base64 = require("base64-js"); + +function decodeEventBody(event) { + if (!event.isBase64Encoded) return event; + event.body = new Buffer.from(base64.toByteArray(event.body)).toString(); + event.isBase64Encoded = false; + return event; +} + +module.exports = { decodeEventBody }; diff --git a/src/handlers/search.js b/src/handlers/search.js index 590fcada..0e4fc8a4 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,3 +1,4 @@ +const { decodeEventBody } = require("./middleware"); const { baseUrl } = require("../helpers"); const { modelsToTargets, validModels } = require("../api/request/models"); const { search } = require("../api/opensearch"); @@ -25,6 +26,7 @@ const getSearch = async (event) => { }; const postSearch = async (event) => { + event = decodeEventBody(event); const eventBody = JSON.parse(event.body); const requestedModels = diff --git a/src/helpers.js b/src/helpers.js index 28f354fe..44f7f818 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,15 +1,19 @@ const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; +function getHeader(event, name) { + return event.headers[name] || event.headers[name.toLowerCase()]; +} + function baseUrl(event) { - const scheme = event.headers["X-Forwarded-Proto"]; + const scheme = getHeader(event, "X-Forwarded-Proto"); // The localhost check only matters in dev mode, but it's // really inconvenient not to have it const host = event.requestContext.domainName === "localhost" - ? event.headers["Host"].split(/:/)[0] + ? getHeader(event, "Host").split(/:/)[0] : event.requestContext.domainName; - const port = event.headers["X-Forwarded-Port"]; + const port = getHeader(event, "X-Forwarded-Port"); let result = new URL(`${scheme}://${host}:${port}`); const stage = event.requestContext?.stage; @@ -20,4 +24,4 @@ function baseUrl(event) { return result.toString(); } -module.exports = { baseUrl }; +module.exports = { baseUrl, getHeader }; diff --git a/tests/fixtures/mocks/collection-1234.json b/test/fixtures/mocks/collection-1234.json similarity index 100% rename from tests/fixtures/mocks/collection-1234.json rename to test/fixtures/mocks/collection-1234.json diff --git a/tests/fixtures/mocks/fileset-1234.json b/test/fixtures/mocks/fileset-1234.json similarity index 100% rename from tests/fixtures/mocks/fileset-1234.json rename to test/fixtures/mocks/fileset-1234.json diff --git a/tests/fixtures/mocks/missing-index.json b/test/fixtures/mocks/missing-index.json similarity index 100% rename from tests/fixtures/mocks/missing-index.json rename to test/fixtures/mocks/missing-index.json diff --git a/tests/fixtures/mocks/missing-work-1234.json b/test/fixtures/mocks/missing-work-1234.json similarity index 100% rename from tests/fixtures/mocks/missing-work-1234.json rename to test/fixtures/mocks/missing-work-1234.json diff --git a/tests/fixtures/mocks/search-multiple-targets.json b/test/fixtures/mocks/search-multiple-targets.json similarity index 100% rename from tests/fixtures/mocks/search-multiple-targets.json rename to test/fixtures/mocks/search-multiple-targets.json diff --git a/tests/fixtures/mocks/search.json b/test/fixtures/mocks/search.json similarity index 100% rename from tests/fixtures/mocks/search.json rename to test/fixtures/mocks/search.json diff --git a/tests/fixtures/mocks/unpublished-work-1234.json b/test/fixtures/mocks/unpublished-work-1234.json similarity index 100% rename from tests/fixtures/mocks/unpublished-work-1234.json rename to test/fixtures/mocks/unpublished-work-1234.json diff --git a/tests/fixtures/mocks/work-1234.json b/test/fixtures/mocks/work-1234.json similarity index 100% rename from tests/fixtures/mocks/work-1234.json rename to test/fixtures/mocks/work-1234.json diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js new file mode 100644 index 00000000..124fba9e --- /dev/null +++ b/test/integration/get-doc.test.js @@ -0,0 +1,93 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; + +describe("Doc retrieval routes", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /works/{id}", () => { + const { handler } = require("../../src/handlers/get-work-by-id"); + + it("retrieves a single work", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + const { event } = helpers + .mockEvent("GET", "/works/{id}") + .pathParams({ id: 1234 }); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.api_model).to.eq("Work"); + expect(resultBody.data.id).to.eq("1234"); + }); + + it("404s a missing work", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const { event } = helpers + .mockEvent("GET", "/works/{id}") + .pathParams({ id: 1234 }); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("404s an unpublished work", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/unpublished-work-1234.json")); + + const { event } = helpers + .mockEvent("GET", "/works/{id}") + .pathParams({ id: 1234 }); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + }); + + describe("GET /collections/{id}", () => { + const { handler } = require("../../src/handlers/get-collection-by-id"); + + it("retrieves a single collection", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + + const { event } = helpers + .mockEvent("GET", "/collections/{id}") + .pathParams({ id: 1234 }); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.api_model).to.eq("Collection"); + expect(resultBody.data.id).to.eq("1234"); + }); + }); + + describe("GET /file-sets/{id}", () => { + const { handler } = require("../../src/handlers/get-file-set-by-id"); + + it("retrieves a single file-set", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-1234.json")); + + const { event } = helpers + .mockEvent("GET", "/file-sets/{id}") + .pathParams({ id: 1234 }); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.api_model).to.eq("FileSet"); + expect(resultBody.data.id).to.eq("1234"); + }); + }); +}); diff --git a/test/integration/search.test.js b/test/integration/search.test.js new file mode 100644 index 00000000..08a3574d --- /dev/null +++ b/test/integration/search.test.js @@ -0,0 +1,113 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const searchHandlers = require("../../src/handlers/search"); +const RequestPipeline = require("../../src/api/request/pipeline"); + +describe("Search routes", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("POST /search/{targets}", () => { + const handler = searchHandlers.postSearch; + const originalQuery = { query: { match_all: {} } }; + const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); + + it("performs a works search by default", async () => { + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + const { event } = helpers + .mockEvent("POST", "/search") + .body(originalQuery); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + + const resultBody = JSON.parse(result.body); + expect(resultBody).to.include.keys(["data", "pagination"]); + expect(resultBody.data.length).to.eq(10); + }); + + it("performs a search on specified models", async () => { + mock + .post("/dc-v2-work,dc-v2-collection/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search-multiple-targets.json")); + const { event } = helpers + .mockEvent("POST", "/search/{models}") + .pathParams({ models: "works,collections" }) + .body(originalQuery) + .base64Encode(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + + const resultBody = JSON.parse(result.body); + expect(resultBody).to.include.keys(["data", "pagination"]); + expect(resultBody.data.length).to.eq(10); + }); + + it("errors if invalid models specified", async () => { + const { event } = helpers + .mockEvent("POST", "/search/{models}") + .pathParams({ models: "works,collections,blargh" }) + .body(originalQuery); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq( + "Invalid models requested: works,collections,blargh" + ); + }); + }); + + describe("GET /search", () => { + const handler = searchHandlers.getSearch; + const originalQuery = { query: { match_all: {} }, size: 10, from: 0 }; + const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); + const searchToken = + "N4IgRg9gJgniBcoCOBXApgJzokBbAhgC4DGAFgPr4A2VCwAvvQDQgDOAlgF5oICMADMzzQ0VVggDaIAO4QMAa3EBdekA"; + + it("requires a searchToken", async () => { + const { event } = helpers.mockEvent("GET", "/search"); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("searchToken parameter is required"); + }); + + it("requires a valid searchToken", async () => { + const { event } = helpers + .mockEvent("GET", "/search") + .queryParams({ searchToken: "Ceci n'est pas une searchToken" }); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("searchToken is invalid"); + }); + + it("performs a search using a searchToken and page number", async () => { + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + + const { event } = helpers + .mockEvent("GET", "/search") + .queryParams({ searchToken, page: 1 }); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + }); + + it("defaults to page 1", async () => { + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + + const { event } = helpers + .mockEvent("GET", "/search") + .queryParams({ searchToken }); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + }); + }); +}); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js new file mode 100644 index 00000000..23ab66d5 --- /dev/null +++ b/test/test-helpers/event-builder.js @@ -0,0 +1,83 @@ +const base64 = require("base64-js"); + +module.exports = class { + constructor(method, route) { + const now = new Date(); + this.event = { + version: "2.0", + routeKey: route, + rawPath: route, + rawQueryString: "", + cookies: [], + headers: { + Host: "api.test.library.northwestern.edu", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https", + }, + queryStringParameters: {}, + requestContext: { + accountId: "123456789012", + apiId: "pewpew", + domainName: "api.test.library.northwestern.edu", + domainPrefix: "api", + http: { + method: method, + path: route, + protocol: "HTTP/1.1", + sourceIp: "127.0.0.1", + userAgent: "Mocha Test", + }, + requestId: "id", + routeKey: route, + stage: "v2", + time: now.toISOString(), + timeEpoch: Number(now), + }, + body: "", + pathParameters: {}, + isBase64Encoded: false, + stageVariables: {}, + }; + } + + body(body) { + switch (typeof body) { + case "undefined": + this.event.body = {}; + case "object": + this.event.body = JSON.stringify(body); + } + return this; + } + + headers(headers) { + Object.assign(this.event.headers, headers); + return this; + } + + pathParams(params) { + this.event.pathParameters = params; + this.event.rawPath = this.event.routeKey; + for (const param in params) { + this.event.rawPath = this.event.rawPath.replace( + `{${param}}`, + params[param] + ); + } + return this; + } + + queryParams(params) { + this.event.queryStringParameters = params; + this.event.rawQueryString = new URLSearchParams(params).toString(); + return this; + } + + base64Encode() { + if (this.event.isBase64Encoded) return this; + this.event.isBase64Encoded = true; + this.event.body = base64.fromByteArray(new Buffer.from(this.event.body)); + return this; + } +}; diff --git a/tests/test-helpers.js b/test/test-helpers/index.js similarity index 76% rename from tests/test-helpers.js rename to test/test-helpers/index.js index 157011f7..6e0eabbe 100644 --- a/tests/test-helpers.js +++ b/test/test-helpers/index.js @@ -1,6 +1,7 @@ const fs = require("fs"); const nock = require("nock"); const path = require("path"); +const EventBuilder = require("./event-builder.js"); global.helpers = { saveEnvironment: () => { @@ -11,6 +12,10 @@ global.helpers = { }); }, + mockEvent: (method, route) => { + return new EventBuilder(method, route); + }, + mockIndex: () => { const mock = nock("https://index.test.library.northwestern.edu"); @@ -27,7 +32,7 @@ global.helpers = { }, testFixture: (file) => { - const fixtureFile = path.join("tests/fixtures", file); + const fixtureFile = path.join("test/fixtures", file); return eval(fs.readFileSync(fixtureFile)); }, }; diff --git a/tests/unit/api/helpers.test.js b/test/unit/api/helpers.test.js similarity index 79% rename from tests/unit/api/helpers.test.js rename to test/unit/api/helpers.test.js index 4bb8b230..67092191 100644 --- a/tests/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -1,6 +1,6 @@ "use strict"; -const { baseUrl } = require("../../../src/helpers"); +const { baseUrl, getHeader } = require("../../../src/helpers"); const chai = require("chai"); const expect = chai.expect; @@ -97,4 +97,24 @@ describe("helpers", () => { ); }); }); + + describe("getHeader()", () => { + it("extracts event headers regardless of case", () => { + const event = { + headers: { + Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", + "X-Forwarded-Proto": "https", + "x-forwarded-port": "443", + }, + requestContext: { + domainName: "abcdefghijz.execute-api.us-east-1.amazonaws.com", + domainPrefix: "abcdefghijz", + stage: "v2", + }, + }; + + expect(getHeader(event, "X-Forwarded-Proto")).to.eq("https"); + expect(getHeader(event, "X-Forwarded-Port")).to.eq("443"); + }); + }); }); diff --git a/tests/unit/api/opensearch.test.js b/test/unit/api/opensearch.test.js similarity index 100% rename from tests/unit/api/opensearch.test.js rename to test/unit/api/opensearch.test.js diff --git a/tests/unit/api/pagination.test.js b/test/unit/api/pagination.test.js similarity index 100% rename from tests/unit/api/pagination.test.js rename to test/unit/api/pagination.test.js diff --git a/tests/unit/api/request/models.test.js b/test/unit/api/request/models.test.js similarity index 100% rename from tests/unit/api/request/models.test.js rename to test/unit/api/request/models.test.js diff --git a/tests/unit/api/request/pipeline.test.js b/test/unit/api/request/pipeline.test.js similarity index 100% rename from tests/unit/api/request/pipeline.test.js rename to test/unit/api/request/pipeline.test.js diff --git a/tests/unit/api/response/opensearch.test.js b/test/unit/api/response/opensearch.test.js similarity index 100% rename from tests/unit/api/response/opensearch.test.js rename to test/unit/api/response/opensearch.test.js diff --git a/tests/unit/aws/environment.test.js b/test/unit/aws/environment.test.js similarity index 100% rename from tests/unit/aws/environment.test.js rename to test/unit/aws/environment.test.js From d0de36c21e1af057f5a976ceecd56809cffdb78e Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Fri, 19 Aug 2022 03:57:34 +0000 Subject: [PATCH 10/61] Create API custom domain and mappings --- src/helpers.js | 35 ++++++++++++---- template.yaml | 42 +++++++++++++++++++ test/integration/get-doc.test.js | 30 +++++++++----- test/integration/search.test.js | 42 +++++++++++++------ test/test-helpers/event-builder.js | 66 +++++++++++++++++++++--------- test/unit/api/helpers.test.js | 25 +++++++++++ 6 files changed, 191 insertions(+), 49 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index 44f7f818..dfe4a0c3 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,5 +1,17 @@ const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; +function isApiGateway(event) { + return gatewayRe.test(event.requestContext.domainName); +} + +function isDefaultRoute(event) { + return event.routeKey.match(/\$default$/); +} + +function isLocal(event) { + return event.requestContext.domainName === "localhost"; +} + function getHeader(event, name) { return event.headers[name] || event.headers[name.toLowerCase()]; } @@ -9,18 +21,27 @@ function baseUrl(event) { // The localhost check only matters in dev mode, but it's // really inconvenient not to have it - const host = - event.requestContext.domainName === "localhost" - ? getHeader(event, "Host").split(/:/)[0] - : event.requestContext.domainName; + const host = isLocal(event) + ? getHeader(event, "Host").split(/:/)[0] + : event.requestContext.domainName; const port = getHeader(event, "X-Forwarded-Port"); let result = new URL(`${scheme}://${host}:${port}`); - const stage = event.requestContext?.stage; - if (gatewayRe.test(result.host) && stage !== "$default") { - result = new URL(`${stage}/`, result); + let stem; + if (isApiGateway(event) && !isDefaultRoute(event)) { + const routeKey = event.routeKey.split(" ")[1]; + const routeRe = new RegExp( + "^(.*)" + routeKey.replace(/\{.+?\}/g, ".+?") + "$" + ); + stem = routeRe.exec(event.rawPath)[1]; + } else if (!isLocal(event) && event?.stageVariables?.basePath) { + stem = event.stageVariables.basePath; + } else { + stem = ""; } + + result = new URL(`${stem}/`, result); return result.toString(); } diff --git a/template.yaml b/template.yaml index 1c884929..dba79b7f 100644 --- a/template.yaml +++ b/template.yaml @@ -11,6 +11,15 @@ Globals: Timeout: 3 Parameters: + CustomDomainCertificateArn: + Type: String + Description: SSL Certificate for the Custom Domain Name + CustomDomainZone: + Type: String + Description: Hosted Zone Name for Custom Domain + CustomDomainHost: + Type: String + Description: Hostname within ApiDomainName for Custom Domain ElasticsearchEndpoint: Type: String Description: Elasticsearch url @@ -18,6 +27,13 @@ Parameters: Type: String Description: Index Prefix Default: "" + V1ApiId: + Type: String + Description: ID of the v1 API to mount on /api/v1 + V1ApiStage: + Type: String + Description: Stage name of the v1 API to mount on /api/v1 + Default: latest Resources: getCollectionByIdFunction: Type: AWS::Serverless::Function @@ -184,3 +200,29 @@ Resources: - OPTIONS MaxAge: 600 StageName: v2 + StageVariables: + basePath: api/v2 + Domain: + DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" + BasePath: api/v2 + CertificateArn: !Ref CustomDomainCertificateArn + Route53: + HostedZoneName: !Sub "${CustomDomainZone}." + v1Mapping: + Type: AWS::ApiGatewayV2::ApiMapping + Properties: + ApiMappingKey: api/v1 + DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" + ApiId: !Ref V1ApiId + Stage: !Ref V1ApiStage +# v1Mapping: +# Type: AWS::ApiGateway::BasePathMapping +# Properties: +# BasePath: api/v1 +# DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" +# RestApiId: !Ref V1ApiId +# Stage: !Ref V1ApiStage +# Outputs: +# V2ApiEndpoint: +# Description: URL to access the v2 API +# Value: !GetAtt dcApiapiv2ApiMapping diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js index 124fba9e..768d09fe 100644 --- a/test/integration/get-doc.test.js +++ b/test/integration/get-doc.test.js @@ -15,9 +15,11 @@ describe("Doc retrieval routes", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/works/{id}") - .pathParams({ id: 1234 }); + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); @@ -31,9 +33,11 @@ describe("Doc retrieval routes", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/works/{id}") - .pathParams({ id: 1234 }); + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(404); }); @@ -43,9 +47,11 @@ describe("Doc retrieval routes", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/unpublished-work-1234.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/works/{id}") - .pathParams({ id: 1234 }); + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(404); }); @@ -59,9 +65,11 @@ describe("Doc retrieval routes", () => { .get("/dc-v2-collection/_doc/1234") .reply(200, helpers.testFixture("mocks/collection-1234.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/collections/{id}") - .pathParams({ id: 1234 }); + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); @@ -79,9 +87,11 @@ describe("Doc retrieval routes", () => { .get("/dc-v2-file-set/_doc/1234") .reply(200, helpers.testFixture("mocks/fileset-1234.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/file-sets/{id}") - .pathParams({ id: 1234 }); + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); diff --git a/test/integration/search.test.js b/test/integration/search.test.js index 08a3574d..dad38cd4 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -18,9 +18,11 @@ describe("Search routes", () => { mock .post("/dc-v2-work/_search", authQuery) .reply(200, helpers.testFixture("mocks/search.json")); - const { event } = helpers + const event = helpers .mockEvent("POST", "/search") - .body(originalQuery); + .pathPrefix("/api/v2") + .body(originalQuery) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); @@ -33,11 +35,13 @@ describe("Search routes", () => { mock .post("/dc-v2-work,dc-v2-collection/_search", authQuery) .reply(200, helpers.testFixture("mocks/search-multiple-targets.json")); - const { event } = helpers + const event = helpers .mockEvent("POST", "/search/{models}") + .pathPrefix("/api/v2") .pathParams({ models: "works,collections" }) .body(originalQuery) - .base64Encode(); + .base64Encode() + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); @@ -47,10 +51,12 @@ describe("Search routes", () => { }); it("errors if invalid models specified", async () => { - const { event } = helpers + const event = helpers .mockEvent("POST", "/search/{models}") + .pathPrefix("/api/v2") .pathParams({ models: "works,collections,blargh" }) - .body(originalQuery); + .body(originalQuery) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(400); @@ -69,7 +75,11 @@ describe("Search routes", () => { "N4IgRg9gJgniBcoCOBXApgJzokBbAhgC4DGAFgPr4A2VCwAvvQDQgDOAlgF5oICMADMzzQ0VVggDaIAO4QMAa3EBdekA"; it("requires a searchToken", async () => { - const { event } = helpers.mockEvent("GET", "/search"); + const event = helpers + .mockEvent("GET", "/search") + .pathPrefix("/api/v2") + .render(); + const result = await handler(event); expect(result.statusCode).to.eq(400); const resultBody = JSON.parse(result.body); @@ -77,9 +87,11 @@ describe("Search routes", () => { }); it("requires a valid searchToken", async () => { - const { event } = helpers + const event = helpers .mockEvent("GET", "/search") - .queryParams({ searchToken: "Ceci n'est pas une searchToken" }); + .pathPrefix("/api/v2") + .queryParams({ searchToken: "Ceci n'est pas une searchToken" }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(400); const resultBody = JSON.parse(result.body); @@ -91,9 +103,11 @@ describe("Search routes", () => { .post("/dc-v2-work/_search", authQuery) .reply(200, helpers.testFixture("mocks/search.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/search") - .queryParams({ searchToken, page: 1 }); + .pathPrefix("/api/v2") + .queryParams({ searchToken, page: 1 }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); }); @@ -103,9 +117,11 @@ describe("Search routes", () => { .post("/dc-v2-work/_search", authQuery) .reply(200, helpers.testFixture("mocks/search.json")); - const { event } = helpers + const event = helpers .mockEvent("GET", "/search") - .queryParams({ searchToken }); + .pathPrefix("/api/v2") + .queryParams({ searchToken }) + .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); }); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index 23ab66d5..c8fbb928 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -3,9 +3,11 @@ const base64 = require("base64-js"); module.exports = class { constructor(method, route) { const now = new Date(); - this.event = { + this._method = method; + this._route = route; + this._event = { version: "2.0", - routeKey: route, + routeKey: `${method} ${route}`, rawPath: route, rawQueryString: "", cookies: [], @@ -29,7 +31,7 @@ module.exports = class { userAgent: "Mocha Test", }, requestId: "id", - routeKey: route, + routeKey: `${method} ${route}`, stage: "v2", time: now.toISOString(), timeEpoch: Number(now), @@ -41,43 +43,69 @@ module.exports = class { }; } + pathPrefix(prefix) { + this._pathPrefix = prefix; + return this; + } + body(body) { switch (typeof body) { case "undefined": - this.event.body = {}; + this._event.body = {}; case "object": - this.event.body = JSON.stringify(body); + this._event.body = JSON.stringify(body); } return this; } headers(headers) { - Object.assign(this.event.headers, headers); + Object.assign(this._event.headers, headers); return this; } pathParams(params) { - this.event.pathParameters = params; - this.event.rawPath = this.event.routeKey; - for (const param in params) { - this.event.rawPath = this.event.rawPath.replace( - `{${param}}`, - params[param] - ); - } + this._pathParams = params; return this; } queryParams(params) { - this.event.queryStringParameters = params; - this.event.rawQueryString = new URLSearchParams(params).toString(); + this._queryParams = params; + return this; + } + + stageVariables(vars) { + this._event.stageVariables = vars; return this; } base64Encode() { - if (this.event.isBase64Encoded) return this; - this.event.isBase64Encoded = true; - this.event.body = base64.fromByteArray(new Buffer.from(this.event.body)); + this._base64Encode = true; return this; } + + render() { + const result = { ...this._event }; + + if (this._base64Encode) { + result.isBase64Encoded = true; + result.body = base64.fromByteArray(new Buffer.from(result.body)); + } + + result.pathParameters = { ...this._pathParams }; + result.rawPath = this._pathPrefix + this._route; + for (const param in result.pathParameters) { + result.rawPath = result.rawPath.replace( + `{${param}}`, + result.pathParameters[param] + ); + } + result.requestContext.http.path = result.rawPath; + + result.queryStringParameters = { ...this._queryParams }; + result.rawQueryString = new URLSearchParams( + result.queryStringParameters + ).toString(); + + return result; + } }; diff --git a/test/unit/api/helpers.test.js b/test/unit/api/helpers.test.js index 67092191..b559d817 100644 --- a/test/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -8,6 +8,8 @@ describe("helpers", () => { describe("baseUrl()", () => { it("extracts the base URL from a local event", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/route/value", headers: { Host: "localhost", "X-Forwarded-Proto": "http", @@ -25,6 +27,8 @@ describe("helpers", () => { it("extracts the base URL from an API Gateway event", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/v2/route/value", headers: { Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", "X-Forwarded-Proto": "https", @@ -44,6 +48,8 @@ describe("helpers", () => { it("extracts the base URL from a CloudWatch event", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/route/value", headers: { Host: "abcdefghijz.cloudfront.net", "X-Forwarded-Proto": "https", @@ -61,6 +67,8 @@ describe("helpers", () => { it("extracts the base URL from an event with a custom domain", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/route/value", headers: { Host: "api.test.library.northwestern.edu", "X-Forwarded-Proto": "https", @@ -80,6 +88,8 @@ describe("helpers", () => { it("prefers a custom domain over localhost", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/route/value", headers: { Host: "localhost", "X-Forwarded-Proto": "http", @@ -96,11 +106,26 @@ describe("helpers", () => { "http://api.test.library.northwestern.edu:3000/" ); }); + + it("properly handles a base path mapping", () => { + const event = helpers + .mockEvent("GET", "/works/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .stageVariables({ basePath: "api/v2" }) + .render(); + + expect(baseUrl(event)).to.eq( + "https://api.test.library.northwestern.edu/api/v2/" + ); + }); }); describe("getHeader()", () => { it("extracts event headers regardless of case", () => { const event = { + routeKey: "GET /route/{param}", + rawPath: "/route/value", headers: { Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", "X-Forwarded-Proto": "https", From af7654c33dc985e450628e6112690032efcf6187 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 22 Aug 2022 18:23:16 +0000 Subject: [PATCH 11/61] Normalize headers to lowercase in middleware --- .gitignore | 1 + src/handlers/get-collection-by-id.js | 2 + src/handlers/get-file-set-by-id.js | 2 + src/handlers/get-work-by-id.js | 2 + src/handlers/middleware.js | 15 ++--- src/handlers/search.js | 5 +- src/helpers.js | 36 +++++++++-- test/integration/search.test.js | 1 + test/test-helpers/event-builder.js | 8 ++- test/unit/api/helpers.test.js | 91 +++++++++++++++++----------- 10 files changed, 111 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index a9f33f38..cb1d4d08 100644 --- a/.gitignore +++ b/.gitignore @@ -206,5 +206,6 @@ $RECYCLE.BIN/ *.lnk samconfig.toml +env.json # End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/src/handlers/get-collection-by-id.js b/src/handlers/get-collection-by-id.js index fd120f46..230e5b46 100644 --- a/src/handlers/get-collection-by-id.js +++ b/src/handlers/get-collection-by-id.js @@ -1,3 +1,4 @@ +const middleware = require("./middleware"); const { getCollection } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -5,6 +6,7 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a Collection by id */ exports.handler = async (event) => { + event = middleware(event); const id = event.pathParameters.id; let esResponse = await getCollection(id); return opensearchResponse.transform(esResponse); diff --git a/src/handlers/get-file-set-by-id.js b/src/handlers/get-file-set-by-id.js index 9d1e759b..abcedee9 100644 --- a/src/handlers/get-file-set-by-id.js +++ b/src/handlers/get-file-set-by-id.js @@ -1,3 +1,4 @@ +const middleware = require("./middleware"); const { getFileSet } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -5,6 +6,7 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a FileSet by id */ exports.handler = async (event) => { + event = middleware(event); const id = event.pathParameters.id; let esResponse = await getFileSet(id); return opensearchResponse.transform(esResponse); diff --git a/src/handlers/get-work-by-id.js b/src/handlers/get-work-by-id.js index 52230a39..96a16ec4 100644 --- a/src/handlers/get-work-by-id.js +++ b/src/handlers/get-work-by-id.js @@ -1,3 +1,4 @@ +const middleware = require("./middleware"); const { getWork } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -5,6 +6,7 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a Work by id */ exports.handler = async (event) => { + event = middleware(event); const id = event.pathParameters.id; let esResponse = await getWork(id); return opensearchResponse.transform(esResponse); diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js index fb8e624f..6ff9246c 100644 --- a/src/handlers/middleware.js +++ b/src/handlers/middleware.js @@ -1,10 +1,7 @@ -const base64 = require("base64-js"); +const { decodeEventBody, normalizeHeaders } = require("../helpers"); -function decodeEventBody(event) { - if (!event.isBase64Encoded) return event; - event.body = new Buffer.from(base64.toByteArray(event.body)).toString(); - event.isBase64Encoded = false; - return event; -} - -module.exports = { decodeEventBody }; +module.exports = function (event) { + let result = normalizeHeaders(event); + result = decodeEventBody(result); + return result; +}; diff --git a/src/handlers/search.js b/src/handlers/search.js index 0e4fc8a4..68f31e8b 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,4 +1,4 @@ -const { decodeEventBody } = require("./middleware"); +const middleware = require("./middleware"); const { baseUrl } = require("../helpers"); const { modelsToTargets, validModels } = require("../api/request/models"); const { search } = require("../api/opensearch"); @@ -7,6 +7,7 @@ const { decodeSearchToken, Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); const getSearch = async (event) => { + event = middleware(event); let token = event.queryStringParameters.searchToken; if (token === undefined || token === "") { return invalidRequest("searchToken parameter is required"); @@ -26,7 +27,7 @@ const getSearch = async (event) => { }; const postSearch = async (event) => { - event = decodeEventBody(event); + event = middleware(event); const eventBody = JSON.parse(event.body); const requestedModels = diff --git a/src/helpers.js b/src/helpers.js index dfe4a0c3..63fa308e 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,5 +1,13 @@ +const base64 = require("base64-js"); const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; +function decodeEventBody(event) { + if (!event.isBase64Encoded) return event; + event.body = new Buffer.from(base64.toByteArray(event.body)).toString(); + event.isBase64Encoded = false; + return event; +} + function isApiGateway(event) { return gatewayRe.test(event.requestContext.domainName); } @@ -12,19 +20,35 @@ function isLocal(event) { return event.requestContext.domainName === "localhost"; } -function getHeader(event, name) { - return event.headers[name] || event.headers[name.toLowerCase()]; +function normalizeHeaders(event) { + if (event.normalizedHeaders) return event; + + const headers = { ...event.headers }; + + for (header in headers) { + const lowerHeader = header.toLowerCase(); + if (header != lowerHeader) { + const value = headers[header]; + delete headers[header]; + headers[lowerHeader] = value; + } + } + + event.headers = headers; + event.normalizedHeaders = true; + return event; } function baseUrl(event) { - const scheme = getHeader(event, "X-Forwarded-Proto"); + event = normalizeHeaders(event); + const scheme = event.headers["x-forwarded-proto"]; // The localhost check only matters in dev mode, but it's // really inconvenient not to have it const host = isLocal(event) - ? getHeader(event, "Host").split(/:/)[0] + ? event.headers["host"].split(/:/)[0] : event.requestContext.domainName; - const port = getHeader(event, "X-Forwarded-Port"); + const port = event.headers["x-forwarded-port"]; let result = new URL(`${scheme}://${host}:${port}`); @@ -45,4 +69,4 @@ function baseUrl(event) { return result.toString(); } -module.exports = { baseUrl, getHeader }; +module.exports = { baseUrl, decodeEventBody, normalizeHeaders }; diff --git a/test/integration/search.test.js b/test/integration/search.test.js index dad38cd4..222f24a6 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -57,6 +57,7 @@ describe("Search routes", () => { .pathParams({ models: "works,collections,blargh" }) .body(originalQuery) .render(); + const result = await handler(event); expect(result.statusCode).to.eq(400); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index c8fbb928..b9f1cd40 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -4,6 +4,7 @@ module.exports = class { constructor(method, route) { const now = new Date(); this._method = method; + this._pathPrefix = ""; this._route = route; this._event = { version: "2.0", @@ -50,10 +51,15 @@ module.exports = class { body(body) { switch (typeof body) { + case "string": + this._event.body = body; + break; case "undefined": - this._event.body = {}; + this._event.body = ""; + break; case "object": this._event.body = JSON.stringify(body); + break; } return this; } diff --git a/test/unit/api/helpers.test.js b/test/unit/api/helpers.test.js index b559d817..13a427f1 100644 --- a/test/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -1,6 +1,10 @@ "use strict"; -const { baseUrl, getHeader } = require("../../../src/helpers"); +const { + baseUrl, + decodeEventBody, + normalizeHeaders, +} = require("../../../src/helpers"); const chai = require("chai"); const expect = chai.expect; @@ -11,9 +15,9 @@ describe("helpers", () => { routeKey: "GET /route/{param}", rawPath: "/route/value", headers: { - Host: "localhost", - "X-Forwarded-Proto": "http", - "X-Forwarded-Port": "3000", + host: "localhost", + "x-forwarded-proto": "http", + "x-forwarded-port": "3000", }, requestContext: { domainName: "localhost", @@ -30,9 +34,9 @@ describe("helpers", () => { routeKey: "GET /route/{param}", rawPath: "/v2/route/value", headers: { - Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", - "X-Forwarded-Proto": "https", - "X-Forwarded-Port": "443", + host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", + "x-forwarded-proto": "https", + "x-forwarded-port": "443", }, requestContext: { domainName: "abcdefghijz.execute-api.us-east-1.amazonaws.com", @@ -51,9 +55,9 @@ describe("helpers", () => { routeKey: "GET /route/{param}", rawPath: "/route/value", headers: { - Host: "abcdefghijz.cloudfront.net", - "X-Forwarded-Proto": "https", - "X-Forwarded-Port": "443", + host: "abcdefghijz.cloudfront.net", + "x-forwarded-proto": "https", + "x-forwarded-port": "443", }, requestContext: { domainName: "abcdefghijz.cloudfront.net", @@ -70,9 +74,9 @@ describe("helpers", () => { routeKey: "GET /route/{param}", rawPath: "/route/value", headers: { - Host: "api.test.library.northwestern.edu", - "X-Forwarded-Proto": "https", - "X-Forwarded-Port": "443", + host: "api.test.library.northwestern.edu", + "x-forwarded-proto": "https", + "x-forwarded-port": "443", }, requestContext: { domainName: "api.test.library.northwestern.edu", @@ -91,9 +95,9 @@ describe("helpers", () => { routeKey: "GET /route/{param}", rawPath: "/route/value", headers: { - Host: "localhost", - "X-Forwarded-Proto": "http", - "X-Forwarded-Port": "3000", + host: "localhost", + "x-forwarded-proto": "http", + "x-forwarded-port": "3000", }, requestContext: { domainName: "api.test.library.northwestern.edu", @@ -121,25 +125,44 @@ describe("helpers", () => { }); }); - describe("getHeader()", () => { - it("extracts event headers regardless of case", () => { - const event = { - routeKey: "GET /route/{param}", - rawPath: "/route/value", - headers: { - Host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", - "X-Forwarded-Proto": "https", - "x-forwarded-port": "443", - }, - requestContext: { - domainName: "abcdefghijz.execute-api.us-east-1.amazonaws.com", - domainPrefix: "abcdefghijz", - stage: "v2", - }, - }; + describe("decodeEventBody()", () => { + it("passes plain text body through unaltered", () => { + const event = helpers + .mockEvent("POST", "/search") + .body("plain body") + .render(); + const result = decodeEventBody(event); + expect(result.isBase64Encoded).to.be.false; + expect(result.body).to.eq("plain body"); + }); + + it("decodes base64 encoded body", () => { + const event = helpers + .mockEvent("POST", "/search") + .body("encoded body") + .base64Encode() + .render(); + expect(event.isBase64Encoded).to.be.true; + expect(event.body).not.to.eq("encoded body"); + + const result = decodeEventBody(event); + expect(result.isBase64Encoded).to.be.false; + expect(result.body).to.eq("encoded body"); + }); + }); + + describe("normalizeHeaders()", () => { + it("converts all headers to lowercase", () => { + const upperHeaders = ["Host", "X-Forwarded-For", "X-Forwarded-Proto"]; + const lowerHeaders = ["host", "x-forwarded-for", "x-forwarded-proto"]; + + const event = helpers.mockEvent("GET", "/search").render(); + expect(event.headers).not.to.include.keys(lowerHeaders); + expect(event.headers).to.include.keys(upperHeaders); - expect(getHeader(event, "X-Forwarded-Proto")).to.eq("https"); - expect(getHeader(event, "X-Forwarded-Port")).to.eq("443"); + const result = normalizeHeaders(event); + expect(result.headers).to.include.keys(lowerHeaders); + expect(result.headers).not.to.include.keys(upperHeaders); }); }); }); From ad921c49f9ca55529813fafcb7ed923fc9f178c6 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 24 Aug 2022 18:58:14 +0000 Subject: [PATCH 12/61] Add /works/{id}/thumbnail route --- package-lock.json | 196 +++++++++++++++++- package.json | 1 + src/aws/environment.js | 13 +- src/handlers/get-work-thumbnail.js | 58 ++++++ template.yaml | 44 +++- test/fixtures/mocks/thumbnail.jpg | Bin 0 -> 12750 bytes .../mocks/work-1234-no-thumbnail.json | 13 ++ test/fixtures/mocks/work-1234.json | 1 + test/integration/get-thumbnail.test.js | 66 ++++++ 9 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/handlers/get-work-thumbnail.js create mode 100644 test/fixtures/mocks/thumbnail.jpg create mode 100644 test/fixtures/mocks/work-1234-no-thumbnail.json create mode 100644 test/integration/get-thumbnail.test.js diff --git a/package-lock.json b/package-lock.json index 7429571e..cdef0ecb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" }, "devDependencies": { @@ -1409,6 +1410,11 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/caching-transform": { "version": "4.0.0", "dev": true, @@ -1675,6 +1681,14 @@ "node": ">=0.3.1" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.211", "dev": true, @@ -2232,6 +2246,54 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -2256,6 +2318,41 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -2373,7 +2470,6 @@ }, "node_modules/ms": { "version": "2.1.3", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -2866,7 +2962,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "dev": true, "funding": [ { "type": "github", @@ -4183,6 +4278,11 @@ "update-browserslist-db": "^1.0.5" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "caching-transform": { "version": "4.0.0", "dev": true, @@ -4347,6 +4447,14 @@ "version": "5.0.0", "dev": true }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "electron-to-chromium": { "version": "1.4.211", "dev": true @@ -4663,6 +4771,49 @@ "version": "2.2.1", "dev": true }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "locate-path": { "version": "6.0.0", "dev": true, @@ -4678,6 +4829,41 @@ "version": "4.4.0", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "log-symbols": { "version": "4.1.0", "dev": true, @@ -4752,8 +4938,7 @@ } }, "ms": { - "version": "2.1.3", - "dev": true + "version": "2.1.3" }, "nanoid": { "version": "3.3.1", @@ -5063,8 +5248,7 @@ } }, "safe-buffer": { - "version": "5.2.1", - "dev": true + "version": "5.2.1" }, "semver": { "version": "6.3.0", diff --git a/package.json b/package.json index 8549ad8b..5dcc3dde 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" }, "scripts": { diff --git a/src/aws/environment.js b/src/aws/environment.js index 4c61d029..c89c99f6 100644 --- a/src/aws/environment.js +++ b/src/aws/environment.js @@ -1,3 +1,14 @@ +const jwt = require("jsonwebtoken"); + +function apiToken() { + const token = { + displayName: ["Digital Collection API v2"], + iat: Math.floor(Number(new Date()) / 1000), + }; + + return jwt.sign(token, process.env.API_TOKEN_SECRET); +} + function elasticsearchEndpoint() { return process.env.ELASTICSEARCH_ENDPOINT; } @@ -12,4 +23,4 @@ function region() { return process.env.AWS_REGION || "us-east-1"; } -module.exports = { elasticsearchEndpoint, prefix, region }; +module.exports = { apiToken, elasticsearchEndpoint, prefix, region }; diff --git a/src/handlers/get-work-thumbnail.js b/src/handlers/get-work-thumbnail.js new file mode 100644 index 00000000..f65ab4e0 --- /dev/null +++ b/src/handlers/get-work-thumbnail.js @@ -0,0 +1,58 @@ +const { apiToken } = require("../aws/environment"); +const axios = require("axios").default; +const middleware = require("./middleware"); +const { getWork } = require("../api/opensearch"); +const opensearchResponse = require("../api/response/opensearch"); + +function getAxiosResponse(url, config) { + return new Promise((resolve) => { + axios + .get(url, config) + .then((response) => resolve(response)) + .catch((error) => resolve(error.response)); + }); +} + +/** + * A simple function to proxy a Work's thumbnail from the IIIF server + */ +exports.handler = async (event) => { + event = middleware(event); + const id = event.pathParameters.id; + const esResponse = await getWork(id); + if (esResponse.statusCode != 200) { + return opensearchResponse.transform(esResponse); + } + + const body = JSON.parse(esResponse.body); + const thumbnail = body?._source?.thumbnail; + + if (thumbnail === undefined) { + return { + statusCode: 404, + body: "Not Found", + }; + } + + const { status, headers, data } = await getAxiosResponse(thumbnail, { + headers: { Authorization: `Bearer ${apiToken()}` }, + responseType: "arraybuffer", + }); + + if (status != 200) { + return { + statusCode: status, + body: data.toString(), + headers: headers, + }; + } + + return { + statusCode: status, + isBase64Encoded: true, + body: data.toString("base64"), + headers: { + "content-type": headers["content-type"], + }, + }; +}; diff --git a/template.yaml b/template.yaml index dba79b7f..9ae3c7e1 100644 --- a/template.yaml +++ b/template.yaml @@ -11,6 +11,9 @@ Globals: Timeout: 3 Parameters: + ApiSecret: + Type: String + Description: Secret Key for Encrypting JWTs (must match IIIF server) CustomDomainCertificateArn: Type: String Description: SSL Certificate for the Custom Domain Name @@ -122,6 +125,36 @@ Resources: ApiId: !Ref dcApi Path: /works/{id} Method: GET + getWorkThumbnailFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-work-thumbnail.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Gets a Work's representative thumbnail. + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + API_TOKEN_SECRET: !Ref ApiSecret + ENV_PREFIX: !Ref EnvironmentPrefix + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /works/{id}/thumbnail + Method: GET searchPostFunction: Type: AWS::Serverless::Function Properties: @@ -215,14 +248,3 @@ Resources: DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" ApiId: !Ref V1ApiId Stage: !Ref V1ApiStage -# v1Mapping: -# Type: AWS::ApiGateway::BasePathMapping -# Properties: -# BasePath: api/v1 -# DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" -# RestApiId: !Ref V1ApiId -# Stage: !Ref V1ApiStage -# Outputs: -# V2ApiEndpoint: -# Description: URL to access the v2 API -# Value: !GetAtt dcApiapiv2ApiMapping diff --git a/test/fixtures/mocks/thumbnail.jpg b/test/fixtures/mocks/thumbnail.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44b71bdb45bb27d7c06e93f3b3619e37df781a8a GIT binary patch literal 12750 zcmb8UWl&tv(k?tOxVu|maCc`QxVt1s2<{pzxCRIe3>wJb5PWcl0KtO=cXxuj1h|~@ zzF&QHf8MUzdp)(fclWb;uiDc6GXJsxXqWf3w*df@mDvDj00005K!5-QAiSdg925x1 z{~ZlpG3EbavscXWzcz@km=N**x3~X-^k4f|hyRr>W^}+l5CHi9Mic-b5fy;+s^j6~ zKo3B~M*!j@yz~O7UOA#7 z{4d%6GJaj@D_v9o+N)L_4}gdO1isFoAfuwcP6Gau2PAxCf;T9?2FN@a`0wVq!ULe717<#^sL@$c~tXCZ(J`f)O2DB09Z>D{eqwlEeCF}!Y z=o0C$hGa_->4+40|8f3-=$lKQ%CI5d$BVv|$;GO}f{_VOB|_3^vqQ^$Yb0P=4`*q< z(*;Cmqiv?r$I1gh@+hooS{C2JoUiloZ*>Z_@TJk9a_p)+o3V-#T8Q#?(!24oc=Z5r z#hw#*UKc!9J|>cos1AdbIHn|qxK}4aTZ!o#`esP1UMyAsYqYA19a@o6exf>nN%57K z>Cbu{N!Iy@8hLhAi`Qt3v52BYXt_&jNQ3#QMefq_i1>ZpQbc(~Cc1#AXnD@YR~veR zSa}Y4yf_LAf<81eyE`~yB0}hasp7z|)=(0K_ezzbi8>$ylx|*m`jjOTgRGD=DI!XH zwa)esTRavgNZH)G{_(eX36M2nPi`w$71q~+P0<}!DQy5tgM z7ls1^Uqonha9#kxsK=#0sAk2FP<8AYuniTw8Q%r?c3Wfl}Pae0}S@fcW*s;cN81u3Ir!#pspY5sT*Q9s8C` zAF7qcQmE6M+b52JPajgr9?Pn&(F>B!?PHNAB?6deqxEqp5-E@sTi`KdvKA@rM#}>T ziF9~X5Tt*FU_wn5`n*UOMjxD?R+B3kS0a1fFSCW!+R~Y?pYQVSm_^-q!q@&j2qZZ` z<+o&*Bd?;P7ex9!P@(<&W{%&Uv*`h%I-N9|9fU{rfc?TP^49b?%gJVuY`fzfquS%x zBkgn3*aIGP2n(-@MK~@01rZtx5ffS% zxwW!=AK*=F_W6UMasY-rUaZg6yM(8ur|CKE%sJHSutkH44a zqFlnQ&S8*U?<9+Fxuf{#c?>MdB@=_3hOy9Q5EGKtYkaG-kB=ZtCO7s^Wo)7~Q~t*m z{O6f?J)!X(78gdT3BNZ|XpY#HBNN{evA5NgnLoHGae8jSxWoW-G`HMZ z4SAHGDWY_K@P+WEkAZtx{ff^Vqi9t#?L9@GtblXLiej7#PAtfQZhmEY9r`mqXA;`> zH8Pz5BIh{N@l1zIrY1iLH_h@x@imwIse0=uH)GWEqTQUDySxKZaV44y;-n?VJdl;J zg$UhLLK)Y$pb}cYrW8alfW@k8+4=!O=WlJ>3YCUxPSLMwjuC{|4$E2eV zpTa5x73|`WBXFa#5$gAl`QdS>K<4}fkR|UR2KiDj_Q!$L8MFmchzOA~U!MjAqWUx7 z(mZ&+0Ct}dv(zWG56YdgneMr)7tP*&0I-6=Tr;?tn%HU0`L&5N51le%wZ>mE$v|J( zPMUk-Zcn?nGLsv#Da(ELn%qG;SmikVrBZ(cFnt9_iBb$*^IJgS!>*?gfBh--+OL{Z z$H&g;PTF+(NPFjS2)S?_j$I*lxTylXa)jmL(bsu{--Q786aM^#_*_9CgzGa6@UJ`X z^I4T(n~jp^$ti$ zZyzG&cmV}h8;w8xeW+rM-+ncYU0u_xpH)w-zSR-ADB(XJwUbn(5u@Pj3jNmh0x&2& zlTTeYz*o*9=w|64t4kM~vRn3;W0^{y^ZvPf-WKl7zK^DEa>U1MA+ z^d3D~mTOaD7XMOGy0ql;U9B}%fa8>q1>gtmv)Z02kSChS`0(yiA54;|1p=5-3+ElE zv+MW0`{zKZI9Xpp!7CV{qab3gM>5cr`SVdj=Ev11ofT__Sl^yTbWA8DXc2BV;d_?% zb(K55O$M~L@xF&`WH0JH-9bD7K52OI3D@^IC*Q42MVD*(R>2fm22C;~$U;BrXP;dA z&y|hYv$qw;e!4cA2{yJ7qEN>NA^w+V zMe)r_JNQL~8hX*g=J9tDJdX)$2FR0`g@?b5)RdjF@5u^&Bv4srg#a2aH2g%C?)7e1 zndI%Om9OmLyT10%WBSUjjQ4D)f4a7;_>e{>#b#yuKs4)8IZ@C4IIQk(HpP&}hvX(t z^*zN8MIy|-sX>9(-jpio_s#7%2gFA>94StD0M25dUjVLdlIv(+99b$(gf+VP(4d}` z8!D{ITXX)ZuT{oMs;wi?=tpfD^e%JyJf|`5&{P-r(Kjt!SQBL9XoJYJdNZjxAZ3>7 zIq^`Jdu+ZYl*hPzJY;md?beBfd}=%dFBV$}it#ZILz9fY{E@j+kt~&I`F4q!W8jao z#IvB*%u}g7iO_EWNj}H0_L??@#u(CMSZ0|+_9}BcaB2R)Pr?bZkpn!dAn>ty+RFD_ zv~FW6FR0N?K$IU788mmlo1RRV$a{|dS580uR6SmWMyMOrPjm>iI+|8KFtMY7FqU3jA2|JG%AQ=> zY^>c3V~4RmqGqv57?z+A$&;r-vF{QhN;|*;Gw``P#s!-v9Pb& z=}hs7BM&JioTLd5V#1Q83w3xh4dl zM!{f|NY_Pi7NJkFX3mmX`$B)62l%RjUgj<{bo!~}n)9Cp%k{AdYd0WO?IFQCukw=} z4Soy*cYBzd5kfuwj7XVD%#SWjTZFhXZxuYpzXsJR6=g318)Qd{c*5#Tkdwe@eb${)X1NS<1ZoJ}qs zcIL{Cp551~Fz3gzT^+W{;rPdDpy>OHx4E#@^vEW?ud9C-1PKy2QZ79fZErChV7{6k zU(sx!BquG0S2Q|3u{+r)^k7{`UyV5&7wz+xMV%J_A9#Za)fIYo6r4FLkN&3nS)tA> zl-?vvQe-z>HP*yPh-+1{Q$6O@jc>dcoQ-GiGEnUTV&O*u&jtSGKX_9*4u6+aQSN$U z)3msU-HpYAhU9&{8>0}$=k#0b%a7s-9h5ZvZ>q2q-N=Eev4(Nm)zM>thMJiM^3l-) z8Mc1FSuz7dCkP^X4gU+zte^R8DijQ-v3Rg1=j0s%LYx4MWp#r^A;&73A>VkvQi9kod_G1b6Uh#@qrh##ZTTwtBe? zowmgu>e_>_3hCUv<36VAtV!Zzo3+i5yjbVQLv@EJKfY$xnmp2I$wZtOuwmSp^SdCf z3C~_g)79D8m220~96nCRaPw7|>;gzu&aCvkhH7HDRX{bY*@P-vQ}kG$hcf+I;G2lVMcDJQCSf)W5 z%ey>5Lh=1|`Xpxn|6h2~EX{R?N$+BzHC1^GMQ z6<7k4=&$O`>1e&7(Ck(Kzi=c-n;)fXXt4(f+{AXGR-bH4A3~^`;x>?B+d!mK*cO1k5`)^HNWqv{Ip8xGS!Wnm{MY7MUcZ-!4;kZE8{C_GPi`?f>_Sj zvw3t*i6E2pdqSN#;TtFDcSn26uHGb4?=k*IMrm4WlnxS!icNV#V{I(?yjgD9djii7F_Bt3FCi{MT9?nRI51JXqtwvywUP5?zqt_IPOYqNyZX3(Iy1`LOJ{cC&*!PgmsLx%=ZX-1%Z+dO&XbJJL#ZG4ciLa= zY#5{aL*+Bu+o{(#;Lr;oH~eu_<+^)7(>#f^M|5B;FKDpop$=Cm{%29S$(uK~cM-sl zE;3YpU1TtVuvg~rTiTIxi4}YyKl?u zUUw4-&n1v8HmXn$>&j39gMAH)#oRc2gtg^_daL{OJz>^r<6>y~MR8HL%*H?gGN51<;8Mlf}9EG-T5xwu4UW`?i-Vc4JAf z1$GE{BreoN))|vj%dgqPi~5MHEeH~bSVWg$K3n1Q=W1T6-{Tbz4@Uk{{*Ei#@901B zkZ2)l(m6ON-j4(k)D;}X?`|J=4U&lf8Dg#Bi}n1Ag0*+MoNN2;|-6k;HuIcOyKWm}fj}YHQWo88T9A6jIg#H4969w4F9D`$j1YTA` zyR;zZl;fM9exOgj!(rbQGl0w@_ZXkPliEOo+TCVtk$h;g-sA)Y*ImGy!TYx0>rv2A zfFFQ?=AaS{*V~;)4g#d8zdC9S+(|ZeLT-5?Y8F8q(k#;FYKfBgoT-8WMY9^T-p21H zCQPs>V6(}vcBIZ~jzwrd0I`aes&g}~)9z=M*irn|AXvS_XFeK&P_8>16f10QTa#hY zC~dYZ+HW6xRW}c`Li|hMJ`^eX0`M!weXk&KM(qVV9Q{cAhDhO}`2B1CRPRnd49>_~#n}1JEsa5nFmZi>4K=st zql$l7YY+P+2UZ#u{Z_ISE0w+a6(E|l6%E8?#>;EpxJZmD+eH?Xuwj=uDlJahe^yUs zi90g|JZg>%-e{oGW!CH~2!<_ahngP81*X1xh&uF691oD(B(4@;ci+*aAj_WI{XJOQ zj_}QuO_o5x{h*&!(IV|@M~}r&DFc1s6zSL>)Q>rATqSY_>|uU%L4qoWQUx7T*O<`- zYH!Se2!rc>GmUB8>=Sw2^wqyPM#SBz#n1+lsk`n0;6Q79uiXMl7mS6gU+a@=J2&O- z-?rjYppT3)WaLsGFwB&`C^$4uBr42vktbvu-pzCtHyk)m%lbGA)C3K!=6&f&HVdTr z(+~!vhbDl1a!w{F;SKjNq{OPlf?K`c+DU9|+TIE8>?cOuOF73U%+TcCC`SM8gB+dL zPp9i&PALL!aQDnK55RWRy7ut*#!Pvv@w*DXw^0VQ8aWNnxx8X5)ermdP7D`9 zlXx{gxui5g)m!b|RarT}-UjTGD{S7_}QX}4DJ7H`bE{;}-Z=5npYO(TB9lG7#y4o9BefjiJCimL;M>74q z1HGJj-lS3gk!|zGwdM=>H)Y0T^u8vgKTQ~i6)%9aMeqy2nD*wwouU3c!&L&&Pa0>; zHCo@?Oa_p8o%pZELJCJ`w~a3`SxMU^3=j}10)I-Yy+2{hVJ02E#N_a2|o=|!)q;5(WoblW9i%UlL?g4bi!F;oe zH+Jsy<}^k9!U=FpJfDwEgyiZ~uUlRKIxm2q4XlFG`7%^ycm~?_jeg8j?w2KIEp&Fn zL*vn`PybG_GwhOt6I0Zr++_OQ6To5<$IyM#y%wdWpa?4xAT}(4>&l2mvAszcy&>Xz z%>h*Zt~jGe7$bwSF}^E$JY%oZCCH-U&GaEpBpGE0knD)%Yqdj+v)k@0@6((6S^v4U z<2sm)Q?R?E%xv&{FSrmaR^;XU^^g?WjgPQ;Y7@v$RwNiYV3*HCo~n1bYc$oS^GAu6 zCx@QA!?&>N7*x~N5~eWR>z?!84&?gbSId>FffObz=^R(W>S>x>X^8J*zsO?1K~D7Q zk8JxI%9bQ1%hSQH7ejeKJ@qRmXmF|!4Mp|#Hm#arxq!NY_-d%X7oDnuDR~f567H6n zTiqYaiHF$*dT-yGDCCfFQ5yZS1HvKHSo~=0)Q{Pljd7ghKEd)k!0&QP+@$*P$e`~q1b8yP*Kk68?sIwqd8A~XmpXfy7?gl>1O+j`}CV8F{ zk6kpSN*>pbZ#e*(GBuB{j+Cl!$gWmdRl3fUOYSu^e{K*jG|Bk5sNa5}JUCutSflfR z{!6ntgpW7JL~p=aRc;F8IlscHRaW7_8wVW%Q)s^Zsl-(flcadtM54Wo!b*8J2w=y> zUg93_vb-oMX&~QXw3G?a1i%9R8XROW+BJD)ZF?TXmQ>)Z2X3N1UxS?tHU+Gs8v%4M zpFSJU)lqkszih{taacKPnibcaDlDksNCT4JNOv={oiuPtCb0adeswESRS)cGp9UnZ zc9D>{Rh|+vK-hAGD$57ss$AW}B+W9hHSQ^U&WLCpRhqyw*P2kw{!!-lGZW_VBLwC= zV`w5j8mp*Rw$$1!yb^&=YC&6i=z^-paY^ztlcHFa{~Tlst6Xen%wxV^*$-_$u8FC5 zcXt;u&T8kty$Dcvh!d9C%A3O%t0axd9JAh?55{0?Q?^vJ@QmKohJU%&Pbl1cS4HoJ zw%|?@G+WkH{zaygN#Vly8~<&liQgxxZ!vivYH@PUFTnD6Tj0rK-Opw}cE0z=Fo?JD z!=CkXyc$MFe^$%a(SOm>*k0MwPCJ^vP!Z;g)#unQ>KPXNMW9zXuhEy6z!85I-1*ylUHg-|mySD@VIV4e^#qmr&|fXR ztTGpW@qJnGb4u{fTF?4P!PsJ_>+?pI3@d5WfSMmr2a?3GwF9B(p+waonC(X9C3Q#D zhq2efsfy=(WxY}E^wrPiun8`ZYV?kM1PV6>oicli5+DF~#i%*+R!FLSlF+oV1yA#b zKoWfUJCwxDLlm@G{oFjcn?&ij73XwjeYV^@)g38J#>ax z>vQsf-tEs>3@^`ZLLqQLR?wi7SkL9K725+1~ zlsx#{l^phCKc0;vQT^+#6q-_-y!wjiTxaZ%9%ZY@O}kH@Y<`?GH}DaL1zSJYcWLcV zSNj@M5G5XYWT{@1E(sCYN@%UqsZ*q26=my!Tnp#-CoTTIzzq-?kjl^d4x~O@u_yX$ z26abN-&|GE(yR_E!DUpW%3csX?ZclhJmYH)!{f>GJyo&K^!1GkDOpz98%9a|=Cgfy zUTVt4EcQtr#lslvSE?#n*K@7?{JoV!epP20~0H!Imv@mBhWSd##`96jzLfF@7%kG0A_ z-eJV8hv!JkI|yZuh`E@BG|vVl3y!s32tWtN8%5r~8S&SIo+ghw83da*BjJaUJ*8~j z3+3bqDhnRP%yG;(aeXWK-E)#CMV}|8^+a>01Q-va%*iOX`Po)K)CFX8znc+4;4II> zviL~k$LQIA*~iM3xOuG2Fiq+{=q32tTCC=pxpbmsE39yR;~c9YDpK0va<^ita#&Ks z-CS%pCKb3pYvBDsCR_Cj4BWO+A6<PM6Rl7`Od>sNGQSEm|Q4x&q(^Uld z?5Byaw`;FDP%~Xoxq)Id!Wzl;2$@+&8D*SQ8>~Cg0V)Hr;;dXw^#>^Iza}a7<$Ib! zPaEDalx-^nG_S{YF7#lCu3(6)_J+AJ0%*LPT5zc=jt~G0*-{Fv z1S_zmZsZC_7Ov5VR%gh3@pAk|*6&_R(bFX=TVnPf>=n{VvP}V@^|(>Uwg%jsvn|eB zZ8g?goG{q=d-XCXtp(t8z@eY(!ja`F@SXPow&wmMXT^w`THvJSVP^d`$ey|bwuS1v z?3$3&d0sI)B4YhZ_(t6<;9UbJ3Z`9}tamzkXdIRK)MDJ~gFK(W&wbvkb*m~5aojBADp`l7N>N8kN>C&(3p>m&4x-qfCW*6F z8@&k^B5}h>sct1u-_#T+WeX2Y>7TmA*HK)U%>|o@e0h8UT$K8?zJ109YKAc?XE3H_ zid?9iEQ(6D*WKvVtmKD-YT5@gxOK3~VGVrZ^AZ6uq(&8pcNF56D zwrNE`wG4wm@|{plZC}aXTb@r>4_@`j9Km_bM&VYL7Ev5hzQo8y>KQ+}(h(JU#HqHn zMn&fc44S6V;VIjp z_6J$7;Rc_+DpV4KCun4*+wC#6DLGMU(LlAl#*w;haFY>u6(s#I8~tnY~>Ofr4&sA@rI`)h*H+F+RHo*kCGE9m^?Z-4Ygz zZ(-gmZYctuEEyi(66l;Aacr< zq%|yVavq7wNGI3hsM(Gm4__VPy0hiQ;SWgFkg-HmEgR!U3Q{oaO#2Gom--=p`l za@)nI(*Bo+*2lQs-;!3=+IN{K)tJwdH$!OK35AG?^SRT_2ak|v0a}aX#}d%N5aLdi zc>>ioVm(f@S5rNE{|~AMc|ze$rEMTXmxvwUlROVGhX zNLwew!V`0|?*&(eSIo!*l(5VrUGDEDKU2(WR003NIxc>{ej3}{guCR~hu|KdB3T)P zPTxXJ_kHk;dHg=uVZsQbP{-=3aGt5(PHL9J%|y1b`USD1AF-WDN;8J68Zuy5s7gau z^0ckcR8_b&G$W9*`k#I4;TewEl&%cSYEcS|VSuf&kb}kC{j5g-%&9lf8?MeLvT$!o z;9=>;DDBN^!=5ju_jmRCrjqNI7#xW)YUgHPKnHvL6&Hnsmck`_vUP8|Kj4$L>*PI( z-WqE4YouU8)dIF8>*?7m^qOk*XuZcF+DML&1?9{5pA#B+SZn1r)}j|=rDXNu8TbMI z->vr&i^1_?&h(43gq+hQ&??4k^;jv1U-cUnge|nADuZHzp9aAS< z`4Ewn?b?`+CJ0o1ItZW~e=0quug5CJL0QdWj*IWj-WIQj+IZjwWNXMrq&mQDXN;dA_zCChBdWZ2MPVr}~B$FPF zS^#8(3>uz|ta6oZI3$!uU3okTGnYS6Q4KK$ZBfNt_*M8_#E0=JC`&x8vUO;678eC| zG`;|Ok6!?r;4^rY@Dr2fPSb{|Y8j?&tzo5e6Q8;@U$qE8iS?aW>!?#<^8?2^%OKJo z*vqefyn>5!nDdu!Y%i886>gTY$@Sr~!fyuO+BV;SFx2!Vm3-=jc4IH^bWe=>9~YcG zH~6tn5!yEGe{7yW>2rW!t(+Aj4w^sN$(=YW~@7hEI z6c36%10ZWkweLd8_kDt~AJ&u_TI1v4MUHHxJv=FRxb9lru+dTNvx(+Sn5ur7iD@#kOB^JY%6^MOrXc!!yQNtVZ)$9tb+V{yZ_{P9 zawX>>Wr*`U6#ujJgv@XFRV{0W&NwgpK8RHm3{V7%`O(BhhMuhfo5^=e&kxggEO-u` zG<-Q$O~)kpWjMD~85>6C_*Ervm?+7Ak(hZylhE$t9`vrQy%pkJt*Og0bkOn_MT1aK zuj37_*X&J2^QDHWut)pdHd&T`Ln*aySgByLf}(W2%zb)-+1A2LS;)vayy zm9(s=PF}00la1Vr`%4cAO9pwOX9Cd$HF1d&%DkYQuD}`C-xXttIXf@$g%d^w@vN`r z0cD|_y%h+4lnm8G;i3{r%E!qXf8}h8A`HKuf|Cb;z# zh2#VYG0=dlZd|`uCNyTsJ72jYRvft1DcHUv4C!v!_M_F_c8GHLpg9NU)L9hPPZReD z;915)A0ofnp8A@nAyjR5!|fF)z@X8f?21Nji0qR>6LOxz57C%VT1%jn|Jl*mY%HnR zx2uUq%3^}ZgQL7OFhHywR674<-@9M5qOssFF~6gPt{pKr2EOgJqs zed3ypyDrOW7+0f@o}#7X&d8!?f%Ft{n4k_BGxT^I;CDYw@m>_o+OGFx3m-STPKw%!o_nbU43h1AO)>dWU|kC;f!@VR!p^)XVrE(X+$aGyk0S zw0O+et$p}Uf|l{tVsI*7V@oSUn@+xYDUrB~&NjPOZ^}#XTR>$QG<@hI?9aQu6eiI@ zNopSJNd`P!Icy>Ml)Y`-p~VB8WOq};k~SAC2HfRGyMR;1O$E?iToUdu(S_fqA>#m2 zDND*|TKhtQ$8s(*jW>w|{sDn@xk!J1pO-z!6sPI-kj-^wtmF1WW1nZW_CE&+^Vve* zpdJmJmD&p$AvecxZ4Ds-a9u;CKK;n@-uAzEdok!c?+3}ws63M2yn4_*MKhclIZlnR?N_-9{4-ty=4jHN_je(>*$xh+M#78&>zOP#Vn zvmkvo6Ac{MgXy;EG*L!qkL>tYyERL7l#Lt|{H8`E{+;|hvh`@%;Q&@@Xi}FFWT|9@ zHtl#Q7B9qpA_3JM@-+K0wqc0sSn1a5&K^oyEaKN*YM(3Z6vZ1e{1n%c(A<1%VTE+L zD;?$(Ol?~O&f~T+SqyYfxVdcN20(F^D=-RqML?_;!YDDdo+pHlMS#FdJd$`GeI!ZCdtj=MTTX{b5FlC^ZH5&b)jMa3#&Gb-6o!Hlq6m81$3^vuDlN%?6At^NVlY8dNLrnLiOc-ecZY zoxzLRybECC^%9^^m`kR5A)2gh~n^qg+eG(UV%ZxY2u83K5%C8`NwJx+2*$}#-}CLgnAQN38?OogwskQI#YT)o$lEY}JE9b*Mtn0j33X@T&CD=bmIo2;!U1C*ITxO}ju zD_M%I1_Gry*DP@iS=Os6?v`&)vz< z1#vgW08~cEkXY%WXv(cvOOEkdd0PMEnzK(--WMSBK>FPu&`}qCV_-np!qjjeR@6Hk z>IxLBGG$u)e-8Akt0G&5n5Q)M+rqkceT(*FTQrx+Z{>tc5QmsF<0;VQ7_ziqnhLy)6766)RE$ literal 0 HcmV?d00001 diff --git a/test/fixtures/mocks/work-1234-no-thumbnail.json b/test/fixtures/mocks/work-1234-no-thumbnail.json new file mode 100644 index 00000000..f2ea0b6d --- /dev/null +++ b/test/fixtures/mocks/work-1234-no-thumbnail.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": true, + "visibility": "Public" + } +} diff --git a/test/fixtures/mocks/work-1234.json b/test/fixtures/mocks/work-1234.json index f2ea0b6d..475b766a 100644 --- a/test/fixtures/mocks/work-1234.json +++ b/test/fixtures/mocks/work-1234.json @@ -8,6 +8,7 @@ "id": "1234", "api_model": "Work", "published": true, + "thumbnail": "https://index.test.library.northwestern.edu/iiif/2/thumbnail/test", "visibility": "Public" } } diff --git a/test/integration/get-thumbnail.test.js b/test/integration/get-thumbnail.test.js new file mode 100644 index 00000000..baa977f6 --- /dev/null +++ b/test/integration/get-thumbnail.test.js @@ -0,0 +1,66 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const { handler } = require("../../src/handlers/get-work-thumbnail"); + +describe("Work thumbnail", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + const event = helpers + .mockEvent("GET", "/works/{id}/thumbnail") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + + beforeEach(() => { + process.env.API_TOKEN_SECRET = "abcdef"; + }); + + it("retrieves a thumbnail", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/thumbnail/test") + .reply(200, helpers.testFixture("mocks/thumbnail.jpg"), { + "Content-Type": "image/jpeg", + }); + + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers["content-type"]).to.eq("image/jpeg"); + }); + + it("returns an error from the IIIF server", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/thumbnail/test") + .reply(403, "Forbidden", { "Content-Type": "text/plain" }); + + const result = await handler(event); + expect(result.statusCode).to.eq(403); + expect(result.body).to.eq("Forbidden"); + }); + + it("returns 404 if the work doc can't be found", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("returns 404 if the work doc has no thumbnail", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234-no-thumbnail.json")); + + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); +}); From c5bd14ee2e833caa1d2484473778f1aaffc31f86 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 29 Aug 2022 15:07:46 +0000 Subject: [PATCH 13/61] Add size and aspect parameters to thumbnail request --- src/handlers/get-work-thumbnail.js | 85 +++++++++++------- .../{thumbnail.jpg => thumbnail_full.jpg} | Bin test/fixtures/mocks/thumbnail_square.jpg | Bin 0 -> 20331 bytes test/fixtures/mocks/work-1234.json | 6 +- test/integration/get-thumbnail.test.js | 79 +++++++++++++--- test/test-helpers/index.js | 60 +++++++------ 6 files changed, 164 insertions(+), 66 deletions(-) rename test/fixtures/mocks/{thumbnail.jpg => thumbnail_full.jpg} (100%) create mode 100644 test/fixtures/mocks/thumbnail_square.jpg diff --git a/src/handlers/get-work-thumbnail.js b/src/handlers/get-work-thumbnail.js index f65ab4e0..6b9394af 100644 --- a/src/handlers/get-work-thumbnail.js +++ b/src/handlers/get-work-thumbnail.js @@ -13,46 +13,71 @@ function getAxiosResponse(url, config) { }); } +function validateRequest(event) { + const id = event.pathParameters.id; + const aspect = event.queryStringParameters.aspect || "square"; + const sizeParam = event.queryStringParameters.size || 300; + const size = Number(sizeParam); + + if (!["full", "square"].includes(aspect)) + throw new Error(`Unknown aspect ratio: ${aspect}`); + if (isNaN(size)) throw new Error(`${sizeParam} is not a valid size`); + if (size > 300) + throw new Error(`Requested size of ${size}px exceeds maximum of 300px`); + + return { id, aspect, size }; +} + /** * A simple function to proxy a Work's thumbnail from the IIIF server */ exports.handler = async (event) => { - event = middleware(event); - const id = event.pathParameters.id; - const esResponse = await getWork(id); - if (esResponse.statusCode != 200) { - return opensearchResponse.transform(esResponse); - } + try { + const { id, aspect, size } = validateRequest(middleware(event)); + const esResponse = await getWork(id); + if (esResponse.statusCode != 200) { + return opensearchResponse.transform(esResponse); + } - const body = JSON.parse(esResponse.body); - const thumbnail = body?._source?.thumbnail; + const body = JSON.parse(esResponse.body); + const iiif_base = body?._source?.representative_file_set?.url; - if (thumbnail === undefined) { - return { - statusCode: 404, - body: "Not Found", - }; - } + if (!iiif_base) { + return { + statusCode: 404, + headers: { "content-type": "text/plain" }, + body: "Not Found", + }; + } - const { status, headers, data } = await getAxiosResponse(thumbnail, { - headers: { Authorization: `Bearer ${apiToken()}` }, - responseType: "arraybuffer", - }); + const thumbnail = `${iiif_base}/${aspect}/!${size},${size}/0/default.jpg`; + + const { status, headers, data } = await getAxiosResponse(thumbnail, { + headers: { Authorization: `Bearer ${apiToken()}` }, + responseType: "arraybuffer", + }); + + if (status != 200) { + return { + statusCode: status, + body: data.toString(), + headers: headers, + }; + } - if (status != 200) { return { statusCode: status, - body: data.toString(), - headers: headers, + isBase64Encoded: true, + body: data.toString("base64"), + headers: { + "content-type": headers["content-type"], + }, + }; + } catch (err) { + return { + statusCode: 400, + headers: { "content-type": "text/plain" }, + body: err.message, }; } - - return { - statusCode: status, - isBase64Encoded: true, - body: data.toString("base64"), - headers: { - "content-type": headers["content-type"], - }, - }; }; diff --git a/test/fixtures/mocks/thumbnail.jpg b/test/fixtures/mocks/thumbnail_full.jpg similarity index 100% rename from test/fixtures/mocks/thumbnail.jpg rename to test/fixtures/mocks/thumbnail_full.jpg diff --git a/test/fixtures/mocks/thumbnail_square.jpg b/test/fixtures/mocks/thumbnail_square.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6150316c2ff85b66c6ac28503dbaded4202ee5d0 GIT binary patch literal 20331 zcmb4qbx>SEv+v>(f|%tEh+$zga$xUo?(h0yrM_@-_M?#A0w`XMh=PFd zA4C2Rp&}w7qo5%G&|jtMxBx^1B&63KG$hnFXh^S?ezgNK9tu7!Di49QCK}x*55o81 zklgAnBH$!FuZ*mgCouz`wxv}>a$e0Dzm8XA%H}!xyKVtF>st6LASnU>>HqoKe*>bt ziWu<#i2orZz<-|qA_PP{S{@{PX-xvU_sE2wJi@DAmH=2t2(QCO!UISGNN#W{fABZg z4N>En(qiJ=^=*g1=^6!yE_}cGFDdF6o62CKZyUZl>U!qCPWr^|z#g1m^65e>{>neA zOl(|Ntv|twuts<{emBbi(#?AYvrLUN0cP~$2YHxX!?s%E8SyS)mT5ur9(`8moiMAD zVgL-!pvT&-o*sNpxJlH_yns4NvzVvRMAUhxT8 zAH7&&c#}etAAO*b9^8=GIp%(DfaIMlT+9{&?<05%hi;l7WoVv`1^xbHFu;?kW#FM^ znUd&VflxcNp+%w%;!)!JKohPkiD?KU>nDgmCz@PqeI10C8D(1nGn{`z+@8A<%45D` z9j&p5=`Qa`2|U&d;anr9G+Hw*7c!8)ilVwG-9R`rj4rHOs&!#{3jzZV^@s&4$wkCi z#IW9Za#NjB9*%u=*f{y7Z75M#-P$Lcj1-2MWC?<^Y-cpJ5QQ3ajp9hfyu1 zicdA~oxW&IxT$Oseh}O}G@(m#`Vu(Doy0b#rVzF42ufMdE}C-AERwK|Og8?c;ieR4 z()WQ_3(7!cdRR2{c|;_hn*AN5gXK3eoe+#n?zX|*QPW_E{@8<-u z)GN0@KN*lDGtZ_q!MAZ`K|?HBk!JVmO)^mCvz4Ovh3K#*g>0sbV+)l!S$B9SNZ9d7 zR^$EtMVFQWnmuo@Nk?`d;ctW|K^`FVRb^4XjU;| zl5qLg${_h@y~dU{t;6e7%}k_iA!HrV?DGA8nrD{{I-tsG^S)~NFxq>hXOSC)KKe7q z+9HwfADa`7eS~S_w+QM~b`b_3ckt6Zh1vT+8m}|qt=TRkDR#TS2H@&%O~QaB|GakvXfbnNjldB6_uBYytiC(W20!V@(~T|*eK zct`C6+|8`2dcXO_^Mcy(#;M3Kgw@UsOC+C{6EVUg<-?fsd2ce0%&%*e%-!*t@m#3( zSLL?mjO!gd#;p!so)^WuPVnMTZcIlGmEBLA{1`c-*zfed#>F!p70a9 zJGeX#m8trnoXgFDQkLdv45FS|gEN-!K5)Lgc)T}Na!KwnIdc2QFoo?edMXMnRa(~A z!u0A)go_fMYczF55{}R*YDw&OZZ80`(ZtHRMr#N!i^N9{+DB9t>Wzd<$Z|(fZ3w6R zqaodSp*yyc5P$;fPD@zDz}bdm;3Cp2?~sknkb~1((}S&Zpx2O=l~j$lOCmiFJ+Mda zyZXKQ^d?SSRiyiRk_1fLiR=&?(m3V)(>RM0=;p)ov<%x=pvUp2W{oSd|L%hM#HG_> z2W09_DtQD{&Ye+>kth7txA!tuWHVKXx;tA!W&bUPC`ktPm|Mc4d6;)atDseUl+*KZ zIJ%)!K}mJxv@siN@uR1XEjt+b2jp`SV$)xbi8};SWH*?ahU>9-g-vlh_6 zWW__#&5dgBk#t+0)YZ?^YiCER?j&yVEv+VH@7ZOZxucy`3VhjFW$3_a{a@}a0DQt{bQ;4&ajjW@<*1R$tfj{wf%w*#X)5jKN?Di^S?-It+HaIfmub<5-tWol3_XM|4un>QpHw=ia!6 zw?AkMj1KPn7O9HN!ILa$P)|f5@jf0|b=Vl)Qr3)5*#)!DmXQ7<`Q6sbb(pZu$zw`TU8J=wlYv6t+63o2Z~W?JQY}=65tcWKY@+z;%R8EdZmY`sb)Z1) z28vvBLm_zeRvWw-YGKI9ejI)gC%Hp-yVw^o$ z$I**9CybT^E~t&T(Bjp{O+e?F*8A*#Kz6)a4T zBU0KJhZ|Iuzs+2{qyk>%js*Xm3H6r5Kkw|bx943joz~nB{31;?$KN7-3K05qAZY{y zf2>U~?rHog(JGI)Czw!dR`DPR6R5iEx~e2zQabr$_{n0jX^^gXu=HEB#!h>kEJ>W= zqRw=QT6rb_aL-Kg0&tDpGMQCf7Hd})+bih1a&LXCZLL@1Vjx2fDaBcJXr`WglE++P z!)^7tpwKk0PgA0q8ijpL)@Nw@w-w`^M6VQ|G`tdl6Zo4d*3-FtKftra_l51$$3&97nBZ4BIlZJ$j*V>7aTV?T5v#hzeFojVR z0tP5DpXw#%)#VIsh-obsxYr6&mUq?ris(pq$W!o>3etp*A5j#BiK>m4*SbV0FXY+3 z5V;}%ZUzb!aBL{+j5Q0xGmR}8XP4CJskx>JWgUus&|**+>o<4yF*{fR{O!3tMRC|e zbt_g4Wj!{3z&Lhl;|(fDJ&tTwTSeVH61lM*mzZU^bxc?@aiY|nfYpQkJ=}1PUGV4J zV?r}SbCtjt^C?qA7uLHnhSO$4 z>91601BG-CC3Vc>BMHW;7TBt!pGp5=WmG^b@AUw^2lrkzF97-X+fUu`E%_p??>R)g zvk&(5?_1-5yBqy5*mH84O$A)X{xHKQ2V zAM2O%Zan*>%^xm3{QLH%Mjvd$6J$Hr&~e2dr>3k1!Wn*Y2?ENMu5xLM^zKr~(Nxh1 z&PDHV%a8mgEPr*O=E=^qzblg8>??Or%I{cIO*t38)HoF{^iC+y;%~@B>5VxkaBn~u z@bp*~e>}hsX(Nt5+-8gx%YFeQ$j#LKE?Qe0AD2u?URVVs^W4u_cP!N3XfiZJ+R_A* ztbFemM!p2iGS(hPmT(BPE*Y{l(pMM`{d-u*$(fV61Lm90*Tr=F`E`_Q57~`MRkjQ{ za%W@QN4EcEfgdf?tT%Y%F8@}rVLXprRvEK&$G1EE$r011@VK(z1t2|hoE`AvcSJmF zBV`=JtY@0vmBoi)=w`B>dwa^iqVK8L(cu$ZHP29i5HN1*sVrvwc116X6kj2cH(I0T zm=sn&SWxBJBm`wyAv!a6*uY^;DL7$oRq*0EV9j>Q+K8e&{A2QwRGXZT=423_v8m2v zY#^~2D=HVQv+$4nyRUl)@2=|dSn$ZZhU_xa_^AbC6U2`VfBVITTpi*Nl7A=lFN|Wi^sl`SWDkv65e47_ z9uVUlg@5VSzSU(0_H&gU5HzMJ91KKj_rHNf|h5A>bWpmhb8fW zsH(P??1l*f9C<1~HcSIB0+rL@5 z=FJsSqgqJ*Ej!FEQ-*w*H!AF;_n`kAd5N5%%y`rPkpp;u@l<*xUV`GI3{#Wp)vFvB z$n-7S7q7j_3*(!wNfs^4(fn5*+$^ZIhC#7B;6z5yNxVO>spMKPJC3=^8t9y$f0a8& zdkPFVoUo@9oV{17Zl10eNE8;+@sUFV9&eT1Tb16s(z!6$O((OnJ8@eLK!IZ5xoW+G zw>D2bB`cTxNLx_B#vqz(Y4|V>7u|Md09C+)L&{+;{m;c~PVn)YJiZ{>$GRbJHPfld z1t^5BK>4`5Fhv62Q40iTLo9hT>M(o(pw|=9qaOo-Q;WR+;#GYZ>pH26oY5YaSQ8~= zQVUkAzZIkc_Z>po9B@%__sPCM%H7B4Xm(G_-u!ybEf8#_{WIUC0KYv_4Srf%XYGU< z&e_V)|88bmCr@PM9TPjR`^9@oqUA@CE&6EhBYh0q{|R+A8t?P>7WIA$B3359q$`6x zRb9)d$n+HVtL(Z(|DX<nY zXNfF?S(AiQyYv6N=F)f)xZk>ehVRb{q8P4`q6K?~{_51oGfL*J8eI67u~%>DS)C*` z&O1E$0{HCaoZ(Y45>#y>AVdyBhl@86-d_V*iNGJ~1}c88JTsPUPMs7%(g2c_%&bbRZNN~O;Zw%6p9kK=)@G`p8oR-prF$dykHzB9~R_Qx`v>m=iB4+=lTW0DtKL_QBAI8>!L@1e!Zt{KU+o8= zFb9V_Qb`5%aZg^Tkr6N3PQV~AV;**u#qDs5yWE4G3b#>PH%JuD$xfI)nlLa}I;4A7 zr(7*zzCweva&Jc%_&0ogXbrS4u5H>L%TPINFmlp92|ChT7Z!gWzBgK;ecPtV#)Fq5 zrS26Dg0{5cf@E<&b4Awqe?leX6zUf%^}mKSNlQ~){7jKhG|!3JsgYRP&*=3Xm%xCI zr#iSf8_HFG$T@G01s)GL)-+W$G@2+nQ!bp5JcnVzmG)iOiL}g&OXvtKJ-V1t_$jv` z>JQ!H9gfAMce-uFLt7!lWhl9E&vKsT$TnbtKd6`?f~ds`|}Pe4WO@t$36@KeMEZWC9wj`%6RB&&@wB zfWju5NV+ciRvO-zr5-4!)*(jQ8bo;fQn8x6-p6EP02xsg~3gJS3PSufs{% zB!*2iPaRBZ`FG+Ki-Aim^PIQ&;f1n4`?n`l41#HGd=^~v$m zx8X~r<-sh5P9(9Yr)dW7LVjtbiaWY9iBqRWQcg)myUoXXc1H*)w3H4CYl~A{P-&diV83v4M z$mcZE6T9Dy56xR3zPA>VU|6vNLgMWg47ldnI?+J7OFadhu^_%qnT@oymB`&R(&Fq} zg|F@7$rY&BgDKaJgc0F`+f%eMmAtjdiY*vrBVWr6epm{NA8p(}ebbisl`jVZb~G~7 zLMZHiCow{V>*i!f3RM{v>%R7;f4WsErE$H_iXkoZCgeSOHok5B4OK}T6_MQ>y6Oiw zdx4_F2Z1AVvE$GRoA@z@W~E!rL<#XI-h$)(kN#uD2O6WCbgaf}NvzxWB%j?o!K_Tv zE!~ff<_fc?8p}!*d8$FtkklriCn~wWIJl721$!$!)`y08RXNBp7KCq_t|i6sH{f{8 z%znxAJcgBnFtEOJVpZ>X?&{>HspF(KM#k2DXHertxzgkDYD&uCdWTrqgmiUV;v%x9 zaX%_x1UxBe#641_NmEhHRYc9vuypc~ z#NE9nJe}l>{{+h?c^;K}cG1oAMRGJ1_HjoNt5pqudh?#=f-z1!>uNRX8I*Uz+_zKN zQ>JuY?W-Ljb{;-huBz(W(4v@q_MTwe#mYiR=mZBENKrfQ%4b$(>_8WRKp&sq6=%j6 zrfW;#N+LW!dU33kQKZEf^*}XhJm>e&vhk*_WH2mh1M5rz32pK4u;Wporn zHgT|-qfJ=vSf3(~#imL{2aR+i1E)FCCO+2JDa&=QF~sze+%|yh;=l1E+Vd_(!!kNj zgXmRj_?nigArbkM6I9;z=&;^ni=8sN;Z|$5=MRtP(KbXJHW_dyuez@yXTRYB9$aMFAWvGeM#ct3r! z9*g23MVFJyh@ga#yJL9FdXW@I35{>=j4%hSI{7^=i;J^+FQA%kVb%0em{84~6xQ4LJv_ zB}{jYeQQQ*&Uh#&D4vit@zI+2LEO%t(L?aKski@KX zaoC>PN*vLdm5Mr(d(WH4iGyt6f<*avxub9ND1B_RBDCNo^(4Nfyk#uy?IVtlpsK_4(x8iLWo5yeteOw4IMf7vI3-4q%cw7ce)BxW4kLczcRzbT zGOh16Z!)bYOTOz*xhAcnA62@nEz#8|QzUfgNyxnuODG7+kEfe<9=@JeUL5e9|I>d? zT+^7cn(U4-?$L;RVazl^s5!gJ?ZUtfi*rJDqy5xy8rr{Gy?}ly!?{XYl=?$0lKN|@ zT)3_@#DH}-F}U1zY9bW9#+e&=9 zBt;z|79BL(bfy-7o^RoQ^~mCX8vqE@CcbH0BV&twvw_%Gg>k3*>&V4W2dzZ=r*JVgAfaRFDAxo#zY94oMN|(KaZTk)>I(pl$YkpL8^b8IWo!px7BgEh0~WW z`QyFSQpDy3J>$0HW^c;D$EX`nBNkK4bKAu~WTqYIQw?k=bxA|47>xL^#W!~+RCP*d zRJzldSp!U?_Zhnqiij;`sT3pZcg|F7liSsdNjOvizgQ&0`kTgcAFXXOWqhYGm0tjV zv!#5oXQa4Vw&$&!36zDGP0XJ9tZ;Fy@9W-xk>kQVHu4$K28g2 zi}8;5IZm!9J!J6jA_1_kENe*X30qqsiHSZ0)Wb1UF~gJw!0tz~yBw1W}l0DpgqfBY4amm7*+tjRO zzg{ot!(wv`yijn7z~e>rnM7zR=UXoAc#BD6^Mdk0i-*uHR?i|a7c%re-hY~(vQQ;> z{1egG1+NRNp*`AXh&=Q^q}( zuq`1zACpiZy{@m`LazZM=yuPixug*$%_@t^+HRn=U*Sde4pNxZ-CeX>`LHbZ$7@|2mTdV!90PMTub%UVa!lkup z7>;$Lt?IHZ_;%#tUohL>IJf&aNcwfqD{QwSDjE^7lGscnvwc_@1uNtU#y)NU=uPI z+Y^-bT&H(DsrYEj)s-36XL6zVf$|UKF&`@T-Gvj&zS0-pTLSTu7mH?C&-w zwm$&8wcQAYu)FeNu+r}KvHVw^FTZVlsek&i!jOw3CW?%U+k$8$;X<|&{(c{9 zfJZ0cK}sDqOQEnyq^jl#NE5I(&iyeU)H@X!%o+&j*Fo1cNJ1hSB)#mjBB-pWu&GHs ztrz_%0Jo3sR=(iPHn2drogubQ-@X^C?az7hp^qJrTM?a@9A60tVAHEjTt1j@AD>d~Y$xLGQ zep9_rRDUVVXg}@g)})qKQan|WV;QOfb@OoRkh)bR+qj^>n(}6G0TF9rq!x)rk4ETR zf-n`5_8W|tw3ZhQJ{B9SEpfEr6Moo(SZat$oc>BV7tT|I%vHQ@+503OvP~jdy$+;u z8tcz}SZ=mi+?A+Wr5>fDEd=*mtxOEbMbHxI1YpEr7A^yz*#`pwOWMmrC#(-yS& z`SauI#d@mJ?B{zS$$gwrZGuY=x1ESE)|?7s>X7CEqQzo=-wn^;F*@^I9+Me$9;*@l z&%#cnCPpjlRYHau+qj~d9SJiv2ddQtiK2xRIUozLW{T8(D3syq7ojKJ2h<&9Ax;Ux z>znT(#U#bc17Y}dYID@7Dj&rN6Dl%fR#u4rzAlm8VVIgNO+eHw^;)E>vM0^}#;ORz z!E;h+A-E*jH*_RuP0q3i5%Il)T_dWZo#jB9FyA7)#3fGb2@C#ospjw{goHY-DCzsb zn_w1N7i@#D;K?_bSJjV&82>UuzTwRmX7hL5>Zu4#OJJKdr)`*{Fv;!QK)d2EJr)%!#GxB46*vSu!GU)0eL#u z3lwfk!cuY>x0Td5hsHR9iz>ntz9{u6bVX+WEu;R}ykyW;U?t(h&(7V4|9&%o)NA=P zXSp5md#&*eR&2XzpypR3N7S%O;)av2FMwK@|ItOL&zA>4I6C}3(yN7N0QZx8c*L&Wbwjw6a>jIQsCW20g72& z|H8Td+l-xip1NU~3FgR5?WD5J$L;;31N}@|BJ!oSM+}j{hKF)2nUG+Aq9SgYIt4v<>aAdSjN`+frc@EteGeue5AA zwQniBANQH*1>jgQi#0Vt(0=v6Q>~gL;IaIgjJu&??ddSwPF=tvK0zx)SY~uMZ25Z# zJ{SlUJ2+V~{2P|Pv?7Zy^{&^YbsWC4zf{V;fZF1_!ETWpYMbMI=xAfx@%dY|1L6d& z#ya-ugojRxQmQpckc>3zkvZs}g&jq< zGCcEs#w>5zs&2-rKB2%3U!zND0xJkf{hE-&in1`Pwzba{4H+Swgdp>=lo#?uQM3Qx z)asM*UIqebn!e}ELZ+5!^DI;5G{{5%-NL+|i|D51%-%H1C?fk_To!w`_TRsSp1eVy z4IjaxNcr$h-c>mnILl5uV>>qG@2qh`3T)(ano5?tQD@fzR@Bs@qBT+A4A(9g&DJx0 zUhIB543j858+28=1I@+d)5$tQ`paUAG0QsPvDnh}sg& zEfy&XJoBd#bmt6u6m(N#kjaLpks_BWuPQBFggf81FrLI*m5jgprbkf0Dj(x@dI> zk5FNu6`_2bKTLN#hvPnKlB1Wm8JIU|JZ1ZGkkR+pp_YmKw9ou9LZd>D_FPL^Xfu3S zofHM_1HJmPlcU4LtucF>AHHTaJ+cVsGv|m>*Z}$%j`IxQtoa=4;3&bOJeBhpKt%Cb z#d0;ij-y1+iJn{1 zH4TyU^tbs=U>wFlqq>A_4GxCQ<^R9@@Yk)nlbISTxIR>Xnzy#T)Hl(kNd2Y;Ia1ywUsQ)C>d zY^ts1Z|D8&9O!S0Jh_`Eo$@tKOB75~v@GF`2!hhe9#L&B>Fxa3PSMk!RAx>>r9~x) zyw&wa>4}|oV0Y(z^+NsfiCOMebr6G{oZFee(v8{F%56V1yx(o8N)kH!go(A$Xvh>4 zn&MeC`Tj6LrJh5oD-HS*H?6PzpV6>nswDOzdr=3 zt&>QNCI48~$=6V>UVh0Y(Kt;)Sqfm<0OXtE$Va~=TVrAhOaUG-$nsEG?et+g{E;PB z8;mDGI1RX5&k(G2UPjnP(Zjpw{dx8oyGB`|qJ}&QqYdwrQ^R9JJ z!+a3u)t7*D*dnqsg#H6*fJMOI6$K{u)C(Y8^E@|;S&_1SZWMqO@DPQ+UqB+SC&D{Xtt$*b3EO>{;h;-a@@wQW0m|Tjyr_%**{gYhEhA2^1$%(Ue;fGp(iM!R)GG3_@{N2Nr3?`j=VHv91~U;u0s@PJ*E$I% zFSMh`*i;#L_|LKleOzQ_$&&*E@&IV%qr@;Z;`{KgUfGKL*oPc2hlIxabm`G@(^@NBjuz|KT zCl(b%Mi5GunFnS+{fkGtsWN@15<2i6r*XBso#I)ADZogm$s~ejEq|YLq&k)a;kQUd z^8RxiF+8P-a&edH+x9yLES_EZ%g(#0HL3ha(;Hs*DW5uqDulXkSix*z`xf%!FmWcw zyVixM9@TjPvqKtzX)B3fvU~O7_`b3Oy_3ZZU4oknp@~(9*3S{Hz5?_I8RTMm>L3ce zk7#61>%A?;%vBDt{nI{7NBcIl5d9_ci?3jxG zo}Q~I>r638&FiycPav2_KT!A)N-qeEzX1C}pdH`$Gqy#PZskA!z+R(LV~M4SOKP!LwkeF`T!j+y*(ICOd!(GhN;@gym4Or)G?dlH z9{VStrz;yr54V9rR0D#C^PSXuRgJe*gA=JnWK104?Q&8FY>57d@lhHMtn>ETFgI9$Cbpj=XWL?GqnjE`W2l@x@ zyc*9bo^y~MECMXW>R9bDvatfYee$}!u8HP!Q7oJoer|^#dJT4$f!4<8%j@Iw!h@bg zLNb&U9*1{Mf!*&oTeZ}V!`BJj3WUdAu?h~`Z_ci$!6d;L5BpODEw6k|D!pqW|TgP*a zt^9Mf&}ilC9Oz#|fz)bO@-w&<8CkKGU7j1CgFuz4VD)OeZ`L2cLF-9KcQ<9TkRWD) z#V<#YZw;)Zx{a!#ITpK(tt(cIg~G)_q8Xz0!Acc`!`obOB~NB*o?@+`d^QCR;^N+y zh4)5fUxw3~b&#Oi+Vp?!>F~%23ZvIv3zR@;#lyu6YB4H*#HT3*1M(cF%okT3a7~ON z;*C!IX~WSX7PjimqFR0jrSHyiRk#+Wt=PE4Z(ETZ4-g6$FL*;*?T=B@E|nH^Rg-&@ zMJ+gp_XN_?w1+6$BW2dm2y$0RNl8tzr5{*5^B^B6Z(@AKxDmrt=W0xLOSY}TTZ9H5 zoz!l={BD&At6!@{eF0o!P=#WK+WzW4Q%tcfo}M0%_=(VPR>;#|JxDar(j$!aXEy&M zK84a*ejwjxJp!K@!9dhTUgOmfm$p+4&&ft4iBmV$l+tV6@oILj5iGCwIpAO{A~g)L z$lWpCzp98Z;tS9`(jTpegwAOUFS3c{v_>?LZW*m;q0%oXAi9-nv{u>c!YoS`jS26V3ZjO~p;UseUjS=Hy!F6N zLV{vJf{opEBi;s~*kIPPNfu|q1JnUshk}3)-_(74*!_40@cT~TC_|?cl+T0=GcMW1 zwd4M&k}B-Ul6dE702hh?{PWc=;+!ga{4cv`XyeV|V_Wo<4OqSI0QJiL z5Qm@T&^$&fe`^CzJw@SRI2R~PDcDgwVfp}{njk<%C*u5z9&(~EMFXlo$u3a^a%xlZ zCpMLEg-H;N_W3iRbPjw^BfJw+sO;<5u#PBN7*1M0??j=Iy@_EzT28T)mC@b!`e|sr z@=So86po~P3bX-M-;-wk21)hghD&s(i^Wn&n)_ItbD(d=aQj2om&2GW3jjRNnZ${>@zq8cK0s(X6F|HeiP&M z5pk++?&CVF+W-%!`Lk0rX`A>qCpS)*`7^6vB2SOty4m?mK)sbF2V;~b^4B!s)l)ci zR6xP!u{UzqXHSiGpxfgQm9-z;A!W_%qO2#9KFrc|>#S`yBzRpP+dBxQBlp$qT9O@F zU=XoLycpJjjYaB8`=_Aj`W2j(fC|gb=vl45JB!>wl=;riGFT6W7DpnE|E|4{mM_*z`zH2 zzOg}H@9WkRDd^~Tn=`IJWH@9G=_c>id=QEwg*NYIZUWy~U zR8@VnZ8R#U-{8B?qXk%YNL0UV-Z{Y8;ZMe)x-7(T*+DV@x%XiOC^p&@KB71;fJA6| zrPUG{H6Zg2JYK_=Hef(_U9bHDz$@>O6Bf`u&~il5X+8rf@Iz8{yChD6bgeT$6)PP{ z=o{Nu(6-rz{16I}!YSeIie7^KF}rsfJWWYo=!q?HC~NH1SgZ|H)QMS{3zLWS0i^F4 z&M|JQ#L9d!i~Lg)Rf)*K{XZJ~$h?bPaS)WKjYl&4)KDz8;mFV4sMgzBIQ&#jvH9QJ zEW~OY#jp23xWy7ufsc-fiVh^T$?ByQI|`ar9V`gpRU?t-r^>qYT*awxWZfrW10!-l zurNG1fpW&B*4;MV&I`co+iW-&a7h8Bt%-VCeK?V0oYn|1 z4cm%mOjLM+izG=L2sO4eaD&vT*LC6kO#DfzCEFac#OcqV610(cj?^j1f^^@1#^OgQ z%hZ^d^Sf~^@vAc_4?9EjRuQ6BnVPx&s?-7GVzza@JsdPzlp_8vl~etzJxNmojNG1K z^(md5rQb!I(7^k?hx9yntf}<=)(}Y7(5{4u?p=^B)g{ec{KZdC&sJ;+q81uo(vGC> zwB2Xi++L7v$3yAAY|{JC17#~z z%Tc1Xer6%gg@U`#`ilK(biREcQiK7-(7E(v1z}tLuOn}Qrjlq)omP0!P`}VGw0}Cz z?bx>MN;qePN6s;#3z0kF8O&oEPJcWwL^6nh3|1T$Tb1|Sqmr7&A}}MR3Ague7541K zICHi9sTG*ciGqH~!kyAu!}2Qc_2sOtg^dSgZJ;^J*}F*b?VoS{MxmBcws}ZdrvJbd zpJ9xxI2ATaYkC_M2U>SmNOwJPDgWtVQGOfLnH0+|<9>~8><9D}{-O%KKszBd! zX{;}Cf@k74A378%pbbU)1^zppZwQ^S9$9%A5k|Rg23bb*Al9oWMRz=nj5Y|;*JH}5 zQu=lFmu^k>ppffCbC6HdL|7iqU3tJkiA{@was|is)pB6^L;1UdpBNm0@#HhA?6V27JquB9;zRecd3V`K%tf40EKsBtW$oe2h zn`2_-nOqD8j%h{>!l9`q_VU1}B?U~w&!qhMRqFC7oUYC*uw&Rt$hZF7K-UZgc@zv9 zTu(pWepYXF0vUuPEMXvOX>kH=^yZt=p+Tg1BK(S`O zb^rwEYCNXAZ~tmbswF;#K{0k!vuOgyr{E^1K^TN9`w(l^q~Hwk#>` zGZD4=jKa?Mo{&Ec?+GaaQR7v3wdR6^U3MuaL?7XpE1Q?5}vDJq|jJUt*| zb~Vxu2}5g#!Q2Au$Fjk?&6*;R@bW~E}2LYv{P4t&K0^K7Dc>mpj&4mA4m z)~HUVJXEV8N@7ps*M#jxXU0w_+um9^@Src$0mXdGkK!Rp6!kfhshZa-bRQLPdk`XD zlh#(fYD8%tznV5+ww~18I47HKTfW9BO1ZB;Xy&j5StwC?3NxMM{G`Nonv1K{s8BOR zQd+V0J7S#-*5FPRtcvWz+oO@4SasNi` zrX|Tpp+-3mKOcF>hlc40Z@Th0gMZ>wBshGTU|jtLFmDhnPE^u(J(vG?G)$$w>c$FY z#`zDWqQrk3t}+k#mR`CVWZIbJ@t`jM1v1{(6dcygnh*k`R~hs%4q2{!y@ICXDPgl` zi(|Daz4B8=nPjM_<@;j$m7WO2@Fe+2^4t`s8d7PPWOBR-8m-h#Z>;38($Gae_%$l2 z!=b;_o?Tr0Q}sz<%fK-5J!e`O1{x5GAMMiq%?J4l-rEkGn}R;o0~}l4tJiegK_IN) zjJ#<$HjN^aa|byEnm}Na#3@p)G*=f#GgGkYhgse5#`S=7qV#R}jrah@*N zk|FaH@ixo*bdX&(H>v}gyHHp0Z%c=b_|lZYV?7r9<~IwN$BEguzl0JX$R2zJ%`~4B zJTAqf2=~R`maZR`e3gSV!=&x5QJ}eh3xeUa4 z5ViD^UNVNjA)QF$ps?wWX1I&Bu{*I8mrJ=LZdBNs9`Wh)J31(mQSuow9#9oOx!L&X zisc?fOoNtlt?e4Ox3%Q(9j&XPb;l<}bcGnrhS{2bAVd@>dAO9(D5Ap; zt2C0BPqF>(`+MororVmS!&k^t;Ipo9nGMaEy?~$$n=aVZUs%!w2YT_=jq2B(&OdtP zVBL6mD?Ef5D*a8_#tA={06)?U$$Vufo^JS%gt>@=q-CF3c=QEKyA{LI-S-#MJZY(n z5Q9BX&W5uSUsy;IM`TyCEz7hdqJHJF56|hI-bh&DsfWd-y0ss1P;8=SQzx3ArY0*N zZd4j2s^_Gqjm~dhxZLXULug#APv@GWvu%dM+;-g`WGA63vT78fLN6QP0-z3@;m}&m z>!>qCCHJtD7(VpNkJI#;^4;^^yZC|O$30L?Xz27d`|tUb%#ZPfPUH;g{aH3A_u}CT z^C=i(i>j)!-FF5}Kiu}cf)Y|UWKmeoWM$fOzYFI({NflUkZMhMq7FQA8KNt2DHlvX zSxJ+KD|F82>5*+8R?07GH$&!c{j>Yq4Shl}RjS`KN=N*AW%7lKP~Erf;Msvs934|7 z%9bnJfx#s)#awIM+o%xG$8`xENmQbMi_$73&-Z9P-37yUK%2@x;%Jpj4IHw5LS2vI^I5(I`s!Fcga72yJdFrOT-x<1c)pelM%{yP(APmtMx2v4G2V9Ff0qnFkE}gQ$;3DMWf6c+7~{f7 zYetwKTlg*RhH$x*^Kl+Fsn2w!$3GgyAms~Rw3~%3lo8PFM*f>M?xL@8CgUjnXg{Yv zr$KiRSjQcDf82PaEEpnh5+V&E*ge$o``)OZwjUtg+lar4*ob|a(r%E%lg%v};H7IS z3HxgAw$sdAku1B>Hd>e-E=Ga9jko@rkGE_%DoshXG-XX_a7=~AVbM^TUnza&`x_5} zIMWPWcZGoM*Yh+fV|M!DimT-c_TLO({3k8?y&K88U+@NAMK{usA+{TNBzpJ+m`jAK z>32OD|5#dbDN&V;=1I^gc%Rp*`I2;?JKV}PQQlg}!k^E_BY$=Ooza2%G9{`H9z{~cf~W)voPR^h@{LE? zwwBik@g2t$S1zJtpH z(!OWt%aJ4+)W+eMTBL6nV}c)hHt(|(W$h2FFLfAn$>*9WVVMJ+k!&ful{>TDNgs7? zMfsW+h30T?*Zu<5Lu<*OA~Gt)M;`GsJ#KD)Xx=2(GI*-sKGg(d3c8L%v<7pWf12<3 z)uQcSNh);6TKLYMV*$z-{!HAF6sHIsh^w_f4 z*&>n*WDfZ6nn|l;MbQN=Vz8G{kp|gVkbWN@zK7fptt0n8ernfQlM`JegAJS$j`dc` zUINV7;~-RLvixq5FH9;L+Gqypgy1k?kQa~EueCj1-aSR8w6u!eW!$V$ecj$nJr8;w z(sd~;A(gF?GbZuNuI?~+rf#m@;#ZPDk(c0v7!^9XSkxFxswK#d8NysH(BpT!0qsKC ztYPl1g}ggsOfd*JUOB4NmEt7E?7q8BDQj~mSyV{akbD0C8h-1liUNyLyEOeKL;GUT zj^9T96t18-)vY}H{#6@8-64kQNWw?6wtR(HT-#4|cMRMFiGgPD&j9&wYpcP_SuS)a zm`8RnPHyBsXB57m4Hk!d;5WMw?kd|&iOggNGDtyUvD>jAdG<8}sX>#cbrO;mPY8lU zECziu+KjOl7=DqG>L}YI-rK$6dt=J9O%+FgBt;$@Gh-g38LZ;=K`Y0!CZ^)83iY-}f*SmM|h z3|2r$864uNWzp{1(@?)y&wz|0=Y}Nth3}7z8&m*(RV0n7PZZN5e`ToaP3a4}>nLs^ zds42T^DqP-LJwo`sbisUfcK4|{{YVvp|dzx+aoH~qw5nB()}eHmoM}}4o{GF+xQOD zq0tv$fLhcaPoxUE>Q0)T9WO~+X-(O;w=VI8Ni7VWre<1&%L|!T z2kUpQX!kxH;b$$gY5VBeIwtz$c)}=wZg6G69}H5zz&%6CC=dQ{swZAaysZ@pc8&q@ z0Q2ugH_&NT8a|(AapA$KCT?ynKxTFl52=CNDu54{daJVbajf6nCAF2Lb6h@l#*%_g z22D!7_F>jgW{xWNwyHljnS&$Fdkfv{&=flqrQu6 zvf?{wP0X<}$K#Es1b|$Q%ejVXqgF{Z9WrfB_UK$)OLierU*6+68+?W^N5YSG{;x>i z8ee6$Lb8qr)UJaSW&u92_*L@ARaI2+jAEZ10jU~g$NY%ahbA=^3m(TjUJ)ecFvg!-QpQh)n*ss8}opd;fb zp!qB+G-tYr7jwCCM`B0=pT?qy32me59zW}v(vP#7{O9Oa%zhIO<6a)l^#E`zE&l-Z zMJN4_ZFnA8r$LYWLHuiA zDoo1RF|?8OjZD++W0<&gVpXxgHKYFk_TKpLbojsZm-y3(?Bh|-yI~0@(pBg2ppr9_ z^bTqBPi}!CX4~X86JJSQ-X@vst42C@oeWW4+Qlu)cu0I!+sR;gCqA``r$A~zkK{4` z0P3&tt+S}@n$OukPut9)UMon^lpNrU@kUPCG#0>}3$E_8UYfgYHsanLL9fEGHo&pA zDaqZR?G3(}=A_?s4!J)|n+YU^wAshL6+mt7%uen-?lJPJZ3m}_=>1Q`y8>p^OkYLZ z2-wh>Lw|c9eSnHqoP17>d9k&g@d+Dt_D#9?=N;*8*xE*2t?5f^d2pe!f;N^iJKzEJ zxb@8#3w5~Dq;|*hatQSRo=!f|RXstV-RX0N)Fa#D!8X(42F?IKtrBTA7js%(T*nJN zuhFHBMphg)0T~~4JgH?8R^+U!)GUlTZ1O83W+upB2bF90?RMVDdP?d$>7k5RtbPO@ z;yEOZ#~kMZvwMvLSi855*jh3e;t4=ENA2IW+b)()ymXB|06opxsjkr!Qz0PrveEZQav@2-f_IswltgjkvT#m#NbNSTMxfE)m zeEE6QcG7Bh`h&t_@g^_yRFU?8K4s`n>zZ=dt@MiP3#8ASNp*WB}m!W>?yL!NK7CD7jh!jWFCQ95hcHzuh~B z;a*Y{@=za$q3st(u+cZJooRLk?n6 zJ!^oBeO2IkRfd~nmzfVFh(FNB>MIzoDI0@`d9D~vK=9X(Q%BaFC#HlB{yc{rneq7n zQ1uHvKF!%vPPKi$at3eKu+#>IC`k7}_!?UH(>BQAmCvFfC-AG0tW6ZVEugf3f2)B- zGj*$K62$wjC&14N?#BuC=yof^mQ|m>YK!f&+hQT3JXCDv! z=sVf0@=w*4#%3QSFiAMv!K%M1Am3FJNu=h`Zd3r=058>BtHR23qHjNou+ z=h`vK>EAg&{AeQM`GfXXOms$DFRd)J+pR7HyR@3%JYXhXU zTZLb%}#}&@@RTbYrv_ug_rCCQBj4mzm0Q4NwKeMeA z^9)O#ey%ZA;Q&*Dcs{h;gse_O9v34fwVH~>v4=a>B+q;digqYcGlhXU?T}4Lh0BwW zI37b4AfOJP>36MWq8g2-1_+7x1IOb_@emv^fDzx5{Au$V`014gJQL@NQ!4fgjxpTU zvrx*ay8Ds9N&f&y*S0F`-jU_Aj5bbb(Gsj=0)5fNG!Qv_61X_`c+Cqn3h<)<7f>nW zh7thU!209?`BPECIRV@cfv+&zj^yLHJenq=YFWYkDbYvwgCEMJ`$tIAptSHCdI*Oj z%yU(Cw(D~?#%4jm_c2nvsLv!lFKcNVks3gwK4f<1%CdPgSOB!xfw9wyq%AZuJ*~^M zxN#{DAR&Cl%njaK@ky9Oy%lbeOOqU#ky-P#yh%m?@)*Iu^{SWG?gpO*mkqU)7SYWd zg`$8gj@^l8?t9R$R&@parmLt}M(EbDOD4p1$X|S(KGJ^rRVQI_q!)WzNbVui4w*D} z43N&!V2^b+u6+CaDsM+Ye{0gZ%4;zAG6>n@35Ep>cqEWcIrX8Amg@Vvs1>d>{USzX zW!!9&BMZTzEl*Xuz35y201`z&){saCG1POwe8QK9y%=vt7>71GW*orzHEpeCs#%VRXxJ)UlYLbxlaJhm1Bj zBhTOk4Rz;G>X%xD#4_EmfhKK{Mgl7HjxYz6KVPsEzNP!!$EIHkd&}#6K6jj!S=jCh z(c_E)K_p;gQGLHwbmoC#)5eg@Ap>)XoyZ5z9v>C+X1Oz^zV$eA>UVkfqI(@)?(?Ot zJa$GVB82Va<215aGL;%PQTtD`)vaY~Z=*xCNLJkbV1T6U2b`am!iBY-yRepdW4E}H zNC?0iOx;((5?9#oH& ZXqP=*s6!xBhRXzJ9th)_R}s{Y|JiLv3m5 { helpers.saveEnvironment(); const mock = helpers.mockIndex(); - const event = helpers .mockEvent("GET", "/works/{id}/thumbnail") .pathPrefix("/api/v2") - .pathParams({ id: 1234 }) - .render(); + .pathParams({ id: 1234 }); beforeEach(() => { process.env.API_TOKEN_SECRET = "abcdef"; @@ -23,12 +21,12 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); mock - .get("/iiif/2/thumbnail/test") - .reply(200, helpers.testFixture("mocks/thumbnail.jpg"), { + .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_square.jpg"), { "Content-Type": "image/jpeg", }); - const result = await handler(event); + const result = await handler(event.render()); expect(result.statusCode).to.eq(200); expect(result.headers["content-type"]).to.eq("image/jpeg"); }); @@ -38,10 +36,10 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); mock - .get("/iiif/2/thumbnail/test") + .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") .reply(403, "Forbidden", { "Content-Type": "text/plain" }); - const result = await handler(event); + const result = await handler(event.render()); expect(result.statusCode).to.eq(403); expect(result.body).to.eq("Forbidden"); }); @@ -51,7 +49,7 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); - const result = await handler(event); + const result = await handler(event.render()); expect(result.statusCode).to.eq(404); }); @@ -60,7 +58,68 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234-no-thumbnail.json")); - const result = await handler(event); + const result = await handler(event.render()); expect(result.statusCode).to.eq(404); }); + + it("accepts a proper size", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/square/!200,200/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_square.jpg"), { + "Content-Type": "image/jpeg", + }); + + const result = await handler(event.queryParams({ size: 200 }).render()); + expect(result.statusCode).to.eq(200); + }); + + it("rejects invalid sizes", async () => { + let result = await handler(event.queryParams({ size: "foo" }).render()); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("foo is not"); + + result = await handler(event.queryParams({ size: 500 }).render()); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("500px"); + }); + + it("accepts proper aspect ratios", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .times(2) + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + let resultFixture = "mocks/thumbnail_full.jpg"; + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(200, helpers.testFixture(resultFixture), { + "Content-Type": "image/jpeg", + }); + + let result = await handler(event.queryParams({ aspect: "full" }).render()); + expect(result.statusCode).to.eq(200); + let expected = helpers.encodedFixture(resultFixture); + expect(result.body).to.eq(expected); + + resultFixture = "mocks/thumbnail_square.jpg"; + mock + .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") + .reply(200, helpers.testFixture(resultFixture), { + "Content-Type": "image/jpeg", + }); + + result = await handler(event.queryParams({ aspect: "square" }).render()); + expect(result.statusCode).to.eq(200); + expected = helpers.encodedFixture(resultFixture); + expect(result.body).to.eq(expected); + }); + + it("rejects improper aspect ratio", async () => { + const result = await handler(event.queryParams({ aspect: "foo" }).render()); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("Unknown aspect ratio: foo"); + }); }); diff --git a/test/test-helpers/index.js b/test/test-helpers/index.js index 6e0eabbe..97ae6188 100644 --- a/test/test-helpers/index.js +++ b/test/test-helpers/index.js @@ -3,36 +3,46 @@ const nock = require("nock"); const path = require("path"); const EventBuilder = require("./event-builder.js"); -global.helpers = { - saveEnvironment: () => { - const env = Object.assign({}, process.env); +function saveEnvironment() { + const env = Object.assign({}, process.env); + + afterEach(function () { + process.env = env; + }); +} - afterEach(function () { - process.env = env; - }); - }, +function mockEvent(method, route) { + return new EventBuilder(method, route); +} - mockEvent: (method, route) => { - return new EventBuilder(method, route); - }, +function mockIndex() { + const mock = nock("https://index.test.library.northwestern.edu"); - mockIndex: () => { - const mock = nock("https://index.test.library.northwestern.edu"); + beforeEach(function () { + process.env.ELASTICSEARCH_ENDPOINT = "index.test.library.northwestern.edu"; + }); - beforeEach(function () { - process.env.ELASTICSEARCH_ENDPOINT = - "index.test.library.northwestern.edu"; - }); + afterEach(function () { + mock.removeAllListeners(); + }); - afterEach(function () { - mock.removeAllListeners(); - }); + return mock; +} - return mock; - }, +function encodedFixture(file) { + const content = testFixture(file); + return new Buffer.from(content).toString("base64"); +} - testFixture: (file) => { - const fixtureFile = path.join("test/fixtures", file); - return eval(fs.readFileSync(fixtureFile)); - }, +function testFixture(file) { + const fixtureFile = path.join("test/fixtures", file); + return fs.readFileSync(fixtureFile); +} + +global.helpers = { + saveEnvironment, + mockEvent, + mockIndex, + encodedFixture, + testFixture, }; From 22931dfac265620c66e33944bc27a131d57458ce Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 29 Aug 2022 22:01:52 +0000 Subject: [PATCH 14/61] Forgot to short-circuit the queryParameters reference --- src/handlers/get-work-thumbnail.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/get-work-thumbnail.js b/src/handlers/get-work-thumbnail.js index 6b9394af..36c4b2d0 100644 --- a/src/handlers/get-work-thumbnail.js +++ b/src/handlers/get-work-thumbnail.js @@ -15,8 +15,8 @@ function getAxiosResponse(url, config) { function validateRequest(event) { const id = event.pathParameters.id; - const aspect = event.queryStringParameters.aspect || "square"; - const sizeParam = event.queryStringParameters.size || 300; + const aspect = event?.queryStringParameters?.aspect || "square"; + const sizeParam = event?.queryStringParameters?.size || 300; const size = Number(sizeParam); if (!["full", "square"].includes(aspect)) From 05ded5287933474f2677c79c3a2ba1054a44b502 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Mon, 29 Aug 2022 19:21:36 +0000 Subject: [PATCH 15/61] Adds a GET '/collections' endpoint that paginates results --- src/api/pagination.js | 5 +- src/handlers/get-collections.js | 47 + src/handlers/search.js | 2 +- template.yaml | 29 + test/fixtures/mocks/collections.json | 1625 +++++++++++++++++++++ test/integration/get-collections.test.js | 46 + test/unit/api/pagination.test.js | 1 + test/unit/api/response/opensearch.test.js | 1 + 8 files changed, 1753 insertions(+), 3 deletions(-) create mode 100644 src/handlers/get-collections.js create mode 100644 test/fixtures/mocks/collections.json create mode 100644 test/integration/get-collections.test.js diff --git a/src/api/pagination.js b/src/api/pagination.js index 09b44872..4ce71801 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -44,14 +44,15 @@ function thisPage(body) { } class Paginator { - constructor(baseUrl, models, body) { + constructor(baseUrl, route, models, body) { this.baseUrl = baseUrl; + this.route = route; this.models = models; this.body = { ...body }; } async pageInfo(count) { - let url = new URL("search", this.baseUrl); + let url = new URL(this.route, this.baseUrl); url.searchParams.set( "searchToken", await encodeSearchToken(this.models, this.body) diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js new file mode 100644 index 00000000..1342bb14 --- /dev/null +++ b/src/handlers/get-collections.js @@ -0,0 +1,47 @@ +const middleware = require("./middleware"); +const { baseUrl } = require("../helpers"); +const { modelsToTargets } = require("../api/request/models"); +const { search } = require("../api/opensearch"); +const opensearchResponse = require("../api/response/opensearch"); +const { decodeSearchToken, Paginator } = require("../api/pagination"); +const RequestPipeline = require("../api/request/pipeline"); + +/** + * A simple function to get Collections + */ +exports.handler = async (event) => { + event = middleware(event); + + let token = event?.queryStringParameters?.searchToken; + + let models = ["collections"]; + let body = ""; + + if (token) { + try { + const request = await decodeSearchToken(token); + const page = Number(event.queryStringParameters.page || 1); + request.body.from = request.body.size * (page - 1); + models = request.models; + body = request.body; + } catch (err) { + return invalidRequest("searchToken is invalid"); + } + } + + const pager = new Paginator(baseUrl(event), "collections", models, body); + const filteredBody = new RequestPipeline(body).authFilter().toJson(); + let esResponse = await search(modelsToTargets(models), filteredBody); + let transformedResponse = await opensearchResponse.transform( + esResponse, + pager + ); + return transformedResponse; +}; + +const invalidRequest = (message) => { + return { + statusCode: 400, + body: JSON.stringify({ message: message }), + }; +}; diff --git a/src/handlers/search.js b/src/handlers/search.js index 68f31e8b..c61e00dc 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -46,7 +46,7 @@ const executeSearch = async (event, models, body) => { return invalidRequest(`Invalid models requested: ${models}`); } - const pager = new Paginator(baseUrl(event), models, body); + const pager = new Paginator(baseUrl(event), "search", models, body); const filteredBody = new RequestPipeline(body).authFilter().toJson(); let esResponse = await search(modelsToTargets(models), filteredBody); let transformedResponse = await opensearchResponse.transform( diff --git a/template.yaml b/template.yaml index 9ae3c7e1..75541745 100644 --- a/template.yaml +++ b/template.yaml @@ -38,6 +38,35 @@ Parameters: Description: Stage name of the v1 API to mount on /api/v1 Default: latest Resources: + getCollectionsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-collections.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Gets Collections. + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + ENV_PREFIX: !Ref EnvironmentPrefix + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /collections + Method: GET getCollectionByIdFunction: Type: AWS::Serverless::Function Properties: diff --git a/test/fixtures/mocks/collections.json b/test/fixtures/mocks/collections.json new file mode 100644 index 00000000..e1d31029 --- /dev/null +++ b/test/fixtures/mocks/collections.json @@ -0,0 +1,1625 @@ +{ + "took": 4, + "timed_out": false, + "_shards": { + "total": 5, + "successful": 5, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 68, + "relation": "eq" + }, + "max_score": 1.0809127, + "hits": [ + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "1e0b26c9-3e0b-4318-8e19-3663ffaf5d9f", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "A collection of 74 photographic images, most 3¼ x 5½ inches, a few smaller. Mixed processes; some black & white, a few sepia tones. Some may be contact prints. Images selectively depicting the construction period of the Panama Canal, 1904–1914. Fifty photographs in the collection document major engineering projects: building of the locks, test transits, Culebra Cut digging, dredging operations, etc. In addition, images of iconic Canal Zone buildings during construction: Administration Building, Tivoli Hotel, Canal Zone towns, etc. The remaining 24 photographs show views of Panama City, Portobelo historical ruins, Taboga Island, bullfighting ring, etc. Social norms of the period are documented in a few images, such as, a sign reading “These Paths for Gold Employees Only” (paths for white employees only.) The collection’s metadata description is provided in English and Spanish thanks to a collaboration between Northwestern University Transportation Library staff and Panama Canal Authority Roberto F. Chiari Library former Library Director Mr. Rolando Cochez and Mr. Isaac E. Carranza, Library Technician.\n\n\nUna colección de 74 imágenes fotográficas, la mayoría de 3¼ x 5½ pulgadas, algunas mas pequeñas. Procesos mixtos; la mayoría en blanco y negro, pocas en tonos sepia. Algunas pueden ser impresiones de contacto. Imágenes presentan el periodo de la construcción del Canal de Panamá, 1904-1914. En esta colección, 50 fotografías documentan grandes proyectos de ingeniería: construcción de las esclusas, tránsitos de prueba, excavación del Corte Culebra, operaciones de dragado, etc. Además, imágenes de icónicos edificios de la Zona del Canal durante su construcción: el Edificio de la Administración, el Hotel Tivoli, pueblos de la Zona del Canal. Las otras 24 fotografías muestran vistas de la Ciudad de Panamá, ruinas históricas de Portobelo, la Isla de Taboga, la plaza de toros, etc. Las normas sociales de la época están documentadas en algunas imágenes por ejemplo: un cartel lee “Estos Caminos Únicamente para Empleados Blancos” (“These Paths for Gold Employees Only.” La descripción de metadatos en esta colección es presentada en Español e Ingles gracias a una colaboración entre la Biblioteca de Transporte de la Universidad de Northwestern y los Sres. Rolando Cochez, pasado Administrador Biblitecario, e Isaac E. Carranza, Técnico Biblitecario, de la Biblioteca Roberto F. Chiari de la Autoridad del Canal de Panamá. ", + "published": true, + "title": "The Panama Canal Construction Photograph Collection", + "modified_date": "2021-09-28T22:09:31.604431Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586293", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/5/resources/1565", + "api_link": "[PLACEHOLDER]/1e0b26c9-3e0b-4318-8e19-3663ffaf5d9f", + "id": "1e0b26c9-3e0b-4318-8e19-3663ffaf5d9f", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.309567Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "67ce0602-e1b4-4d5b-9028-1d772f2c7917", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Elizabeth Augusta Wrightson Collection consists of 135 arias and other songs chiefly from Italian operas of the bel canto style. Approximately 100 contain detailed performance markings including ornamentation, expression marks and cadenzas.", + "published": true, + "title": "Elizabeth Augusta Wrightson Collection of Annotated Bel Canto Vocal Music", + "modified_date": "2021-08-12T13:29:43.193496Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.587322", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/3/resources/1356", + "api_link": "[PLACEHOLDER]/67ce0602-e1b4-4d5b-9028-1d772f2c7917", + "id": "67ce0602-e1b4-4d5b-9028-1d772f2c7917", + "representative_image": {}, + "create_date": "2021-04-28T13:22:55.818169Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "e4075581-b388-4f2c-afc5-4860e172ba24", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": " Architectural Drawings from the collections of Northwestern University Libraries.", + "published": true, + "title": "Architectural Drawings", + "modified_date": "2021-06-28T15:12:00.641129Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682023", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/e4075581-b388-4f2c-afc5-4860e172ba24", + "id": "e4075581-b388-4f2c-afc5-4860e172ba24", + "representative_image": {}, + "create_date": "2021-04-22T17:09:20.594040Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "29354d03-c3db-443e-a18d-9a15f04558b6", + "_score": 1.0809127, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "This collection was from the Study Photographs Collection which was once in the Visual Media Center under the Department of Art History at Northwestern University. ", + "published": true, + "title": "Department of Art History || Study Photographs Collection", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682772", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/29354d03-c3db-443e-a18d-9a15f04558b6", + "id": "29354d03-c3db-443e-a18d-9a15f04558b6", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.420485Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "1f9fe6a4-9059-4e1d-9a1f-178892beaf13", + "_score": 1.0809127, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "La Caricature was a satirical weekly published French periodical that was distributed in Paris between 1830 and 1843 during the July Monarchy. This collection contains works from when Charles Philipon (1800–61) was director and main author. The complete volumes of La Caricature can be found in HathiTrust Digital Library. These are individual plates only. Related URL connects to the complete volume. ", + "published": true, + "title": "La Caricature (1830-1835; Charles Philipon, founding editor)", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683613", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/1f9fe6a4-9059-4e1d-9a1f-178892beaf13", + "id": "1f9fe6a4-9059-4e1d-9a1f-178892beaf13", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.400123Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "58432865-db82-4b05-8910-453fe1df5972", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Audio recordings of Kongo Langlois Roshi, one of the first American Zen Buddhist priests.", + "published": true, + "title": "The Dharma Talks of Kongo Langlois Roshi", + "modified_date": "2022-02-03T23:00:08.248263Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683888", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/58432865-db82-4b05-8910-453fe1df5972", + "id": "58432865-db82-4b05-8910-453fe1df5972", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.977859Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5a3744f2-5cbc-4edc-bb16-1bad7265e41c", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Charles Deering McCormick of Special Collections contains a collection of about 8,000 items relating to the Siege and Commune of Paris of 1870-1871. This collection is the largest held on this subject in the United States, and one of the largest in the world. This diverse and visually exciting array includes original photographs, caricatures, posters, etchings, lithographs, books, newspapers, manuscripts and realia created during and in reaction to l'année terrible: the epoch-changing year that witnessed France's defeat in the Franco-Prussian War, the downfall of the Second Empire, the hardship and suffering of the four-month siege of Paris, and the bloody civil war that snuffed out the Commune uprising.\n\nThe nucleus of the collection was acquired in 1971, the centenary year of end of the Commune, when astute Northwestern University librarians purchased almost en bloc the catalogue offerings of a prominent French book dealer. Since that time it has been augmented by many successive acquisitions. Along with the American Civil War and the Crimean War, the Franco-Prussian War was one of the first to be documented photographically. Photographs of the war and of the ruined Paris landscape it and the struggle with the Communards caused abound, as well as some of the earliest examples of manipulated fake photographs created for propaganda purposes. ", + "published": true, + "title": "The Siege and Commune of Paris, 1870-1871", + "modified_date": "2022-01-31T19:08:45.445939Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684028", + "api_model": "Collection", + "finding_aid_url": "https://libguides.northwestern.edu/c.php?g=115015&p=749162", + "api_link": "[PLACEHOLDER]/5a3744f2-5cbc-4edc-bb16-1bad7265e41c", + "id": "5a3744f2-5cbc-4edc-bb16-1bad7265e41c", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.480650Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "87099ea1-eef0-42d2-a3d2-7b203dc3774c", + "_score": 1.0809127, + "_source": { + "featured": false, + "keywords": ["poster"], + "visibility": "Public", + "description": "These posters represent a sampling (approximately 2200) of our growing collection of over 5000 Africana posters housed at the Melville J. Herskovits Library of African Studies. The subjects covered here include politics, health, religion, liberation struggles, art exhibitions, and social issues.", + "published": true, + "title": "Posters from the Herskovits Library", + "modified_date": "2021-10-13T16:18:06.405769Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764162", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/87099ea1-eef0-42d2-a3d2-7b203dc3774c", + "id": "87099ea1-eef0-42d2-a3d2-7b203dc3774c", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.443176Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "ce1916b7-9fb8-44b5-80a1-5459ca97190c", + "_score": 1.0809127, + "_source": { + "featured": null, + "keywords": ["photography"], + "visibility": "Public", + "description": "U.S. Army Base Hospital # 12 (Chicago Unit), frequently referred to as the Northwestern University Base Hospital, was organized in July 1916 to assist the medical needs of the Allied forces in Europe during WWI. Records of the United States Army Base Hospital Number 12 World War I and II are located at the Northwestern University Archives and include clippings, programs, poems, newsletters, journals, published articles, memorabilia, correspondence, photographs, and a scrapbook. Support for this project provided by the Pritzker Military Museum & Library.", + "published": true, + "title": "United States Army Base Hospital Number 12 World War I and II Records", + "modified_date": "2022-02-24T23:51:16.506259Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.845491", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/ce1916b7-9fb8-44b5-80a1-5459ca97190c", + "id": "ce1916b7-9fb8-44b5-80a1-5459ca97190c", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/79d55f3c-d74a-4710-8fee-5b29280398af", + "workId": "f2b8af41-f4ea-4ab3-8ae2-9f6a5d5e1caf" + }, + "create_date": "2021-03-12T02:11:32.402606Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "55ff2504-dd53-4943-b2cb-aeea46e77bc3", + "_score": 1.0068047, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Edward Sheriff Curtis published The North American Indian between 1907 and 1930 with the intent to record traditional Native American cultures. The work comprises twenty volumes of narrative text and photogravure images. Each volume is accompanied by a portfolio of large photogravure plates. Search tip: shortcut to a list of just the text volumes by searching \"illustrated books\" in the search bar.\n\nCultural Context: Content on this site is drawn from a historical source which includes materials that may contain offensive images or language reflecting the nature of Settler Colonialism in America. Such materials should be viewed in the context of the time and place in which they were created. The images and text in this site are presented as specific, original artifacts recording the attitudes, perspectives and beliefs of a different era. Northwestern University does not endorse the views expressed in this collection which may contain images and text offensive to some researchers.", + "published": true, + "title": "Edward S. Curtis's The North American Indian", + "modified_date": "2021-11-01T16:37:01.894554Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.585594", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/55ff2504-dd53-4943-b2cb-aeea46e77bc3", + "id": "55ff2504-dd53-4943-b2cb-aeea46e77bc3", + "representative_image": {}, + "create_date": "2021-04-09T15:49:10.195808Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "f359687a-e496-404b-8708-9bbe490521dd", + "_score": 1.0068047, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Project Survival Teach-out focused on the battle to defend the environment. The Teach-out features an assembly of environmental authorities and politically knowledgeable speakers.", + "published": true, + "title": "Project Survival Films, 1970", + "modified_date": "2022-02-08T17:36:51.114921Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.585730", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/f359687a-e496-404b-8708-9bbe490521dd", + "id": "f359687a-e496-404b-8708-9bbe490521dd", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.947509Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "070e81e2-f176-42d7-8b21-55c16b3a5378", + "_score": 1.0068047, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Musical Scores from the collections of Northwestern University Libraries.", + "published": true, + "title": "Musical Scores", + "modified_date": "2021-09-16T21:42:10.035361Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586139", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/070e81e2-f176-42d7-8b21-55c16b3a5378", + "id": "070e81e2-f176-42d7-8b21-55c16b3a5378", + "representative_image": {}, + "create_date": "2021-04-21T14:02:35.341785Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "cf5faa52-b2ff-4edf-932b-ab07e0cdb7a7", + "_score": 1.0068047, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "This collection was from the Study Photographs Collection which was once under the Department of Theatre at Northwestern University. ", + "published": true, + "title": "Department of Theatre || Study Photographs Collection", + "modified_date": "2022-02-24T23:51:15.863394Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682155", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/cf5faa52-b2ff-4edf-932b-ab07e0cdb7a7", + "id": "cf5faa52-b2ff-4edf-932b-ab07e0cdb7a7", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/35d06946-da0a-4c1d-906f-877cf64f03c3", + "workId": "eb83430b-12ae-4afd-80b7-03a6607d981f" + }, + "create_date": "2021-03-12T02:11:32.355892Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c373ecd2-2c45-45f2-9f9e-52dc244870bd", + "_score": 1.0068047, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "The Commedia dell'Arte, the famous improvisational theatre style born in Renaissance Italy, remains a major influence in today's theatre. Antonio Fava is an actor, comedian, author, director, musician, mask maker and Internationally renowned Maestro of Commedia dell'Arte. The masks in this collection are all stored in the Charles Deering McCormick Library of Special Collections. Fava's book The Comic Mask in the Commedia dell'Arte is published by Northwestern University Press.", + "published": true, + "title": "Commedia dell'Arte: The Masks of Antonio Fava", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683644", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/c373ecd2-2c45-45f2-9f9e-52dc244870bd", + "id": "c373ecd2-2c45-45f2-9f9e-52dc244870bd", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.451164Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "aab3574c-e17d-43f2-93ab-fb6298bc0fc5", + "_score": 1.0068047, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Transportation Library Audiovisual Collection", + "published": true, + "title": "Transportation Library Audiovisual Collection", + "modified_date": "2022-02-15T19:23:24.115203Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684335", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/aab3574c-e17d-43f2-93ab-fb6298bc0fc5", + "id": "aab3574c-e17d-43f2-93ab-fb6298bc0fc5", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.982410Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "51d4475f-5a0a-42a4-8901-bde73a1fae99", + "_score": 1.0068047, + "_source": { + "featured": null, + "keywords": ["Evanston"], + "visibility": "Public", + "description": "James (Jim) Roberts earned his bachelor’s degree from Northwestern University, master’s and Ph.D. degrees from the University of Iowa, and an M.B.A. from Duke’s Fuqua School of Business. Until recently he served as the Executive Vice Provost for Finance and Administration at Duke University, where he was also Adjunct Professor in the Department of History. The James Roberts photographs range in date from 1968-1972, the years when Roberts was a student at Northwestern. Although Roberts was a history major, his primary passion was photography, and he considers its practice a substantial part of his education. Roberts arrived at Northwestern in 1968. He quickly became acquainted with the photography community in Evanston, and in 1969 joined the staff of the Syllabus yearbook. During 1969-1972, Roberts documented Northwestern life intensively, and made occasional forays into the surrounding Evanston and Chicago environs. He also spent a quarter studying abroad in Cuernavaca, Mexico at the Centro Intercultural de Documentation, an important site of Catholic social activism in Mexico. The late 1960s and early 1970s were remarkable years at Northwestern, in Chicago, and around the world. The photos Roberts took, many published in the ’70, ’71, and ’72 editions of the Syllabus, artfully documented both the mundane (i.e. Evanston street scenes, football games, dining hall kitchen staff) and the extraordinary (i.e., the March on Washington, Vietnam War protests, student clashes with police, and street life in Mexico).", + "published": true, + "title": "Jim Roberts Photographs, 1968-1972", + "modified_date": "2022-02-24T23:51:16.606088Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.613829", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/51d4475f-5a0a-42a4-8901-bde73a1fae99", + "id": "51d4475f-5a0a-42a4-8901-bde73a1fae99", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/de8cabf7-990a-4cf5-8801-1a6753ede5d4", + "workId": "fea61a1f-0473-4cef-adcb-728f0b5b2cc4" + }, + "create_date": "2021-03-12T02:11:32.418085Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "30d59215-8f2b-470d-967b-5a721fa366b7", + "_score": 1.0068047, + "_source": { + "featured": null, + "keywords": ["photography"], + "visibility": "Public", + "description": "Ann C. Gunter is an art historian and Bertha and Max Dressler Professor in the Humanities at Northwestern University. Her work addresses the visual and material cultural of the ancient Near East and its Eastern Mediterranean neighbors. These images are from her personal slide collection.", + "published": true, + "title": "Ann C. Gunter Slide Collection", + "modified_date": "2022-02-24T23:51:17.186411Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.614113", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/30d59215-8f2b-470d-967b-5a721fa366b7", + "id": "30d59215-8f2b-470d-967b-5a721fa366b7", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/fcf13c01-70f0-4f99-9b30-6a25b9a23b54", + "workId": "99df692f-4408-46d8-966a-c80cf662f5b7" + }, + "create_date": "2021-03-12T02:11:32.358572Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "10fcd5dc-7550-4e5f-87ba-44cc778b1bbe", + "_score": 1.0068047, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": null, + "published": true, + "title": "Sample Public A/V Collection", + "modified_date": "2022-03-14T14:48:39.218179Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.614799", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/10fcd5dc-7550-4e5f-87ba-44cc778b1bbe", + "id": "10fcd5dc-7550-4e5f-87ba-44cc778b1bbe", + "representative_image": {}, + "create_date": "2022-03-14T14:48:39.218179Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "740d080c-2a49-4abc-acb9-b8af362a2910", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Materials showcasing typography and graphic art from the collections of Northwestern University Libraries.", + "published": true, + "title": "Typography and Graphic Art", + "modified_date": "2021-06-28T18:00:23.490474Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.585532", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/740d080c-2a49-4abc-acb9-b8af362a2910", + "id": "740d080c-2a49-4abc-acb9-b8af362a2910", + "representative_image": {}, + "create_date": "2021-04-08T21:06:23.368736Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "d3a8e587-cc58-4cb0-aea2-65465d42ec3e", + "_score": 0.65805584, + "_source": { + "featured": true, + "keywords": ["featured"], + "visibility": "Public", + "description": "The Ira Silverman Railroad Menu Collection consists of 238 items: 227 menus and eleven pamphlets. 35 United States and Canadian railroads are represented in the collection. All menus were issued by their respective railroad.\n", + "published": true, + "title": "Ira Silverman Railroad Menu Collection", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586630", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/d3a8e587-cc58-4cb0-aea2-65465d42ec3e", + "id": "d3a8e587-cc58-4cb0-aea2-65465d42ec3e", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.413134Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "eaa3047f-685b-4a52-8e38-e11e4792faba", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Ray Still Master Class Audio Archives", + "published": true, + "title": "Ray Still Master Class Audio Archives", + "modified_date": "2022-02-02T18:23:24.814103Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586722", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/eaa3047f-685b-4a52-8e38-e11e4792faba", + "id": "eaa3047f-685b-4a52-8e38-e11e4792faba", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.952918Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "8bdddde5-39a2-4179-baf0-cb7e1cfc1e9f", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Moldenhauer Collection is comprised of music manuscripts, sketches and quotations, correspondence, photographs, newspaper clippings, printed programs, manuscript texts and other materials which document composers and musicians active between 1683 and 1973. This collection is a selection of items that were digitized for a researcher from Box 3, Folder 28.", + "published": true, + "title": "Moldenhauer Collection ", + "modified_date": "2022-02-18T17:14:30.706129Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.587187", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/3/resources/1167", + "api_link": "[PLACEHOLDER]/8bdddde5-39a2-4179-baf0-cb7e1cfc1e9f", + "id": "8bdddde5-39a2-4179-baf0-cb7e1cfc1e9f", + "representative_image": {}, + "create_date": "2021-10-13T14:41:59.841972Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "4ed2338d-c715-4a86-8ac6-6b4030a42be5", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": ["poster"], + "visibility": "Public", + "description": "Hamid Naficy Middle Eastern Movie Posters Collection contains 249 posters, which have been digitized and cataloged individually. These posters document the social history of film in Iran and offer a unique visual representation of the political and social climate there between 1966 and 2014.", + "published": true, + "title": "Hamid Naficy Iranian and Middle Eastern Movie Posters Collection", + "modified_date": "2021-11-03T19:04:23.758063Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.587445", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/4ed2338d-c715-4a86-8ac6-6b4030a42be5", + "id": "4ed2338d-c715-4a86-8ac6-6b4030a42be5", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.440684Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "fbe609ee-0fab-472d-bc98-51dce3077167", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "51 master class audio recordings of Arnold Jacobs (low brass) from the 1970s - early 80s. ", + "published": true, + "title": "Arnold Jacobs Master Class Audio Archives", + "modified_date": "2022-02-03T17:13:50.372422Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682491", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/fbe609ee-0fab-472d-bc98-51dce3077167", + "id": "fbe609ee-0fab-472d-bc98-51dce3077167", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.803754Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c2a8a3e0-af0f-4e04-8721-91698fc14574", + "_score": 0.65805584, + "_source": { + "featured": null, + "keywords": ["Evanston"], + "visibility": "Public", + "description": "Series 31/6/155, Photographs: Folder 4 - The following images are from the events of the May 3, 1968 Bursar’s Office Takeover, the first student protest at Northwestern University. On April 22, 1968, members of Black student organizations, For Members Only (FMO) and African American Student Union (AASU), presented a list of demands to the Northwestern University administration regarding discriminatory campus policies and practices. When the demands were not met, on May 3, 1968, approximately 120 African American students occupied the Bursar’s Office, Northwestern University’s business office. The demonstration lasted for 38 hours when members of FMO and AASU and the administration ultimately reached a compromise on May 4, 1968. This effort successfully led to the “May 4th agreement” in which the University promised enhanced support for African American students regarding admission, financial aid, housing, curriculum, and counseling, as well as an official statement by the administration admitting to the existence of institutional racism at the University. Notably, it also prompted the creation of the Department of African American Studies and a social space for Black students on campus, known as the Black House.", + "published": true, + "title": "Records of the Bursar’s Office Takeover, May 1968", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683203", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/c2a8a3e0-af0f-4e04-8721-91698fc14574", + "id": "c2a8a3e0-af0f-4e04-8721-91698fc14574", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.448212Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "442cc5c7-2c72-481c-a84f-eca6ef976505", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Leigh Bienen Videos", + "published": true, + "title": "Leigh Bienen Videos", + "modified_date": "2022-02-04T15:02:57.438602Z", + "admin_email": "lbbienen@law.northwestern.edu", + "indexed_at": "2022-08-24T17:29:14.683425", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/442cc5c7-2c72-481c-a84f-eca6ef976505", + "id": "442cc5c7-2c72-481c-a84f-eca6ef976505", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.876787Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "65209b41-8f6a-4aee-8666-9676bafbf8f7", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "University Archives Film Collection", + "published": true, + "title": "University Archives Film Collection", + "modified_date": "2022-02-18T13:34:03.654220Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684410", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/65209b41-8f6a-4aee-8666-9676bafbf8f7", + "id": "65209b41-8f6a-4aee-8666-9676bafbf8f7", + "representative_image": {}, + "create_date": "2022-02-17T21:10:21.239254Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "8cdf83c9-3831-4211-acd7-122bca9b89da", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "This collection of Northwestern University football films dates between 1929 and 1984 with a gap for the period 1932–1934. The films record actual intercollegiate games although for many seasons not all contests are represented. The films are most complete for football seasons from the 1950s through the 1980s; beginning in the late 1950s, practice, junior varsity, and freshman team game films often are included. Northwestern team and Big Ten Conference football highlight films are scattered across the collection from the 1930s through the 1970s.", + "published": true, + "title": "Athletic Department Football Films", + "modified_date": "2022-02-18T14:58:48.206131Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684527", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/772", + "api_link": "[PLACEHOLDER]/8cdf83c9-3831-4211-acd7-122bca9b89da", + "id": "8cdf83c9-3831-4211-acd7-122bca9b89da", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.894184Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5f6e9e67-7f2d-4208-b3bd-b1aedcdf7e35", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Poetry and Poetics Colloquium", + "published": true, + "title": "Poetry and Poetics Colloquium", + "modified_date": "2022-02-04T17:55:03.098667Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764217", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/5f6e9e67-7f2d-4208-b3bd-b1aedcdf7e35", + "id": "5f6e9e67-7f2d-4208-b3bd-b1aedcdf7e35", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.943123Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c4f30015-88b5-4291-b3a6-8ac9b7c7069c", + "_score": 0.65805584, + "_source": { + "featured": false, + "keywords": ["Evanston"], + "visibility": "Public", + "description": "Over 150 photographic prints (mostly 4x6 cabinet size, mounted on cardstock) taken by noted Evanston photographer Alexander Hesler, dating circa 1870-1887 and depicting Northwestern University faculty, students, and buildings. Includes 2 copies of published volume \"Photographic Views of Picturesque Evanston\" (1887).", + "published": true, + "title": "Alexander Hesler Photograph Collection", + "modified_date": "2022-02-24T23:51:16.277400Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.845377", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/546", + "api_link": "[PLACEHOLDER]/c4f30015-88b5-4291-b3a6-8ac9b7c7069c", + "id": "c4f30015-88b5-4291-b3a6-8ac9b7c7069c", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/d9736e00-a675-4e60-a35a-9e48c09e5340", + "workId": "4f5f0e6f-a32d-4ecf-b960-937506b2f22a" + }, + "create_date": "2021-03-12T02:11:32.433108Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "d4671cda-6ed8-48b9-8031-88b5940d572e", + "_score": 0.65805584, + "_source": { + "featured": true, + "keywords": ["featured"], + "visibility": "Public", + "description": "This collection of airline, railroad and cruise ship menus began as a gift from Northwestern alumnus George M. Foster, who donated his extensive menu collection to the Transportation Library in 1997, where it has since been expanded from other sources.", + "published": true, + "title": "Transportation Library Menu Collection", + "modified_date": "2022-02-24T23:51:16.283849Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.845411", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/5/resources/1230", + "api_link": "[PLACEHOLDER]/d4671cda-6ed8-48b9-8031-88b5940d572e", + "id": "d4671cda-6ed8-48b9-8031-88b5940d572e", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/e93a7c4a-1228-463d-8569-6dece7748486", + "workId": "9ad49656-a88b-468b-aafd-00152814a8fc" + }, + "create_date": "2021-03-12T02:11:32.381243Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "faf4f60e-78e0-4fbf-96ce-4ca8b4df597a", + "_score": 0.65805584, + "_source": { + "featured": null, + "keywords": ["poster"], + "visibility": "Public", + "description": "View more than 300 World War II-era posters issued by various U.S. government agencies. They represent the government's effort, through art, illustration and photographs, to unite the American people in a time of adversity.", + "published": true, + "title": "World War II Poster Collection", + "modified_date": "2022-02-24T23:51:16.558814Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.613720", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/faf4f60e-78e0-4fbf-96ce-4ca8b4df597a", + "id": "faf4f60e-78e0-4fbf-96ce-4ca8b4df597a", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/eb6499c9-3b01-4457-823b-32b2d6063316", + "workId": "c4a32218-22f2-49cd-8a67-949cd00c22d3" + }, + "create_date": "2021-03-12T02:11:32.445651Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "1fb2f5bc-780e-4abb-b61e-fe6d715d13e8", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": null, + "published": true, + "title": "ABC.456.TEST", + "modified_date": "2022-03-04T15:56:28.566313Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586234", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/1fb2f5bc-780e-4abb-b61e-fe6d715d13e8", + "id": "1fb2f5bc-780e-4abb-b61e-fe6d715d13e8", + "representative_image": {}, + "create_date": "2022-03-04T15:55:40.703986Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "690b42c7-4e7d-40ba-a864-dd300e53a331", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": null, + "published": true, + "title": "ABC.123.TEST", + "modified_date": "2022-03-14T20:10:08.622664Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586270", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/690b42c7-4e7d-40ba-a864-dd300e53a331", + "id": "690b42c7-4e7d-40ba-a864-dd300e53a331", + "representative_image": {}, + "create_date": "2022-03-04T15:04:45.999466Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "d389d9ca-27e3-44fc-a12f-3a5a4208a6c8", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Imagination without Borders introduces the work of Japanese visual artist TOMIYAMA Taeko and, to a lesser extent, the paintings and prints of MARUKI Toshi & MARUKI Iri and Eleanor RUBIN. All four think of themselves as political artists and see their work as a protest against social injustice and the suffering such injustice causes. All four were deeply affected by World War II and their art reflects their shared belief that war is a disaster for everyone.", + "published": true, + "title": "Imagination without Borders", + "modified_date": "2022-02-03T14:48:23.707445Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586925", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/d389d9ca-27e3-44fc-a12f-3a5a4208a6c8", + "id": "d389d9ca-27e3-44fc-a12f-3a5a4208a6c8", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.860151Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "2c9a427b-de57-4bff-98c8-a5082bc82285", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Materials in this collection of 45 documents pertain to the institution of slavery in the Americas. The documents include correspondence on the slave trade, assignments on chain gangs, bills of sale, manumission papers, and wills and bequests. They also include letters providing character references for free Black people, reports on a school in an African American religious community in Baltimore, Maryland, and an indenture agreement between a printer and an apprentice.", + "published": false, + "title": "Slavery, Enslaved Persons, and Free Blacks in the Americas Collection", + "modified_date": "2022-02-09T22:18:54.045438Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.587297", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1188", + "api_link": "[PLACEHOLDER]/2c9a427b-de57-4bff-98c8-a5082bc82285", + "id": "2c9a427b-de57-4bff-98c8-a5082bc82285", + "representative_image": {}, + "create_date": "2022-01-20T15:27:16.810205Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "b315b92d-4757-44f5-b311-b59eac4fa03e", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Frederick Douglass (c. 1817-1895) was an abolitionist, orator, and writer. In 1838, Douglass escaped from his Maryland enslaver, and over time became one of the most celebrated abolitionists and social reformers of the 19th century. This collection of 11 original documents and 6 copies contains Frederick Douglass’ bill of sale, correspondence, newspaper clippings, and additional copies of correspondence.", + "published": false, + "title": "Frederick Douglass Collection ", + "modified_date": "2022-02-09T22:17:17.249236Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682281", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1738", + "api_link": "[PLACEHOLDER]/b315b92d-4757-44f5-b311-b59eac4fa03e", + "id": "b315b92d-4757-44f5-b311-b59eac4fa03e", + "representative_image": {}, + "create_date": "2022-01-20T15:27:36.381059Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "cd92fb8d-76a7-4e72-be0f-78a5839caa89", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "This collection contains lavishly illustrated early U.S. bicycle and bicycle parts manufacturers’ catalogs covering the period from 1890 through 1932, with the great majority published between 1890 and 1902. The 71 items in this collection represent over 40 manufacturers, roughly geographically distributed in equal parts between the Chicago area, the East Coast, and the Midwest.", + "published": true, + "title": "Late 19th - Early 20th Century Bicycle and Bicycle Parts Catalogs", + "modified_date": "2021-07-15T20:33:46.985909Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682585", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/cd92fb8d-76a7-4e72-be0f-78a5839caa89", + "id": "cd92fb8d-76a7-4e72-be0f-78a5839caa89", + "representative_image": {}, + "create_date": "2021-04-21T19:42:43.255698Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "48cefdba-c111-4a6a-8d40-98ba7986e2d6", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Robert Marcellus Master Class Audio Archives", + "published": true, + "title": "Robert Marcellus Master Class Audio Archives", + "modified_date": "2022-02-03T21:45:31.271729Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682689", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/48cefdba-c111-4a6a-8d40-98ba7986e2d6", + "id": "48cefdba-c111-4a6a-8d40-98ba7986e2d6", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.967937Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "1c2e2200-c12d-4c7f-8b87-a935c349898a", + "_score": 0.48835278, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "This collection features digital copies of 113 antique maps of Africa and accompanying text dating from the mid-16th Century to the early 20th Century. All scanned maps are authentic and originally collected by the Melville J. Herskovits Library of African Studies. Melville J. Herskovits established Northwestern University's Program of African Studies in 1948 (the first of its kind at a major research university in the United States). The Herskovits Library, formally created as a separate library in 1954, has since its inception collected maps that describe Africa from their earliest appearance to the most current. Map area coverage includes the continent, regions (particularly North Africa and Algeria), islands (particularly Madagascar), and a few city plans. All of these maps are loose items, though many have been excised from published atlases. Some of the highlights of the digital collection are: a series of Ptolemic maps of North Africa by Ruscelli, ca. 1565; Forlani: Africa , 1562; Mercator: Africa, 1595; Blaeu: Æthiopia ca. 1650 (a Prester John map); Carey: Africa, 1795 (first map of Africa published in the United States), Arrowsmith: Africa, 1802 (notable for its large dimensions, 124 x 145 cm.), a series of Algerian maps published by the French government in the mid-1800's, and maps by other notable cartographers, such as Hondius, Jansson, de Jode, de L'Isle, Ortelius, Sanson, and de Wit. The original maps are kept and maintained in the map collection in the Government & Geographic Information Collection. We welcome questions, comments, and suggestions concerning any aspect of this digital collection, particularly with regards to provenance. Other antique maps from the Herskovits Library which were not included in this digital collection are either duplicate copies or other editions, such as French government sets covering Algeria.", + "published": true, + "title": "16th-Early 20th Century Maps of Africa", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682738", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/1c2e2200-c12d-4c7f-8b87-a935c349898a", + "id": "1c2e2200-c12d-4c7f-8b87-a935c349898a", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.395086Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "0af7b1f5-9aee-4ddc-b3b8-ede6eac027c7", + "_score": 0.48835278, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Scrapbooks from the collections of Northwestern University Libraries.", + "published": true, + "title": "Scrapbooks", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683179", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/0af7b1f5-9aee-4ddc-b3b8-ede6eac027c7", + "id": "0af7b1f5-9aee-4ddc-b3b8-ede6eac027c7", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.370858Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "db98ed75-1810-46d3-a838-176c0685cd01", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The James E. \"Jimmy\" Johnson Scrapbooks collection consists mainly of two scrapbooks. The first, bearing the title “Football Note,” contains numerous newspaper articles detailing Johnson’s football career and social engagements during the years he spent at Northwestern University. About half of the scrapbook chronicles activities associated with the Carlisle Indian Industrial School. Included are numerous photographs and halftone pictures depicting the school and its students. Scattered throughout the scrapbook are printed items and ephemera pertaining to school ceremonies and events. Additionally there are pages with business and personal information cards documenting Johnson’s friends and associates.\n\nThe second scrapbook is smaller and only contains newspaper clippings pertaining to Johnson’s football career at Carlisle.\n\nSupplementing the scrapbooks are additional photographs and biographical information, including Johnson's death notice.", + "published": true, + "title": "James E. \"Jimmy\" Johnson (1879-1942) Scrapbooks and Miscellanea", + "modified_date": "2021-09-28T22:18:28.037587Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684644", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/587", + "api_link": "[PLACEHOLDER]/db98ed75-1810-46d3-a838-176c0685cd01", + "id": "db98ed75-1810-46d3-a838-176c0685cd01", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.460047Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5c9eaad8-afb8-4224-ae1a-cffddc731109", + "_score": 0.48835278, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Vincent Cichowicz Master Class Audio Archives", + "published": true, + "title": "Vincent Cichowicz Master Class Audio Archives", + "modified_date": "2022-02-04T21:08:02.399142Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684921", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/5c9eaad8-afb8-4224-ae1a-cffddc731109", + "id": "5c9eaad8-afb8-4224-ae1a-cffddc731109", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.992073Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c708f479-db91-4585-8267-874c5e7da73f", + "_score": 0.48835278, + "_source": { + "featured": null, + "keywords": ["photography"], + "visibility": "Public", + "description": "Images taken from: Narrative report, July 1935 to April 1936. Illinois: Works Progress Administration, Illinois, Women and Professional Division, 1936.", + "published": true, + "title": "WPA Digital Collection, 1935-1943", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764090", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/c708f479-db91-4585-8267-874c5e7da73f", + "id": "c708f479-db91-4585-8267-874c5e7da73f", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.410084Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "85af945f-1396-4b88-9087-c4f2fa64d21c", + "_score": 0.48835278, + "_source": { + "featured": null, + "keywords": ["photography"], + "visibility": "Public", + "description": "Rob Linrothe is an Associate Professor in the Department of Art History at Northwestern University. Here he shares his images of Tibetan, Chinese, and Indian monuments, field photography, and architecture. The collection has a strong focus on Tibetan sites, particularly petroglyphs, stupas, and monasteries in the Ladakh and Zangskar regions. Linrothe has also photographed the architecture and sculpture of monuments such as Borobudur in Indonesia and Sanchi in India. Rob Linrothe specializes in Buddhist art of the Himalayas. His recent fieldwork has focused on pre-modern mural painting in the Ladakh and Zangskar regions of Western Tibet (northwest India), as well as the contemporary revival of monastic painting in the Amdo province of Eastern Tibet (China).", + "published": true, + "title": "Rob Linrothe Image Collection", + "modified_date": "2022-02-24T23:51:15.805348Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764292", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/85af945f-1396-4b88-9087-c4f2fa64d21c", + "id": "85af945f-1396-4b88-9087-c4f2fa64d21c", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/4c1c9ab6-72ed-42fa-b273-6acd0cf946dc", + "workId": "f292521f-4114-4d89-b477-403a697df2f8" + }, + "create_date": "2021-03-12T02:11:32.427741Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "35c3a71a-ce83-4410-82ec-640e01da86eb", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Press cutting books from the Dublin Gate Theatre", + "published": false, + "title": "Dublin Gate Theatre Archive", + "modified_date": "2021-06-28T18:06:19.151525Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586211", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/1410", + "api_link": "[PLACEHOLDER]/35c3a71a-ce83-4410-82ec-640e01da86eb", + "id": "35c3a71a-ce83-4410-82ec-640e01da86eb", + "representative_image": {}, + "create_date": "2021-06-22T20:53:44.226666Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "267bed1b-f808-4c51-acb1-0288378819d2", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Northwestern University Libraries’ Map & Atlas Collection allows access to an ever-growing selection of our cartographic resources in high resolution.", + "published": true, + "title": "Map and Atlas Collection", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586396", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/267bed1b-f808-4c51-acb1-0288378819d2", + "id": "267bed1b-f808-4c51-acb1-0288378819d2", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.386638Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "ba35820a-525a-4cfa-8f23-4891c9f798c4", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Gift of the Deering Family. These sketches by Catalan artist Ramón Casas (1866-1932) document the work of an under-represented artist who was influential in modernist art circles in Barcelona and Paris along with his peers John Singer Sargent, Henri de Toulouse-Lautrec, and the young Pablo Picasso. Three bound hardcover volumes of sketches came to the Northwestern University Libraries in 1998 as part of a donation of books that had belonged to NU benefactor Charles Deering (1852-1927). Deering and Casas met in 1904 and established a lifelong friendship. At some point before the donation, a few hundred loose Casas drawings, postcards, and illustrated letters dating from 1891 to 1912 were bound into their current form. There were at least five volumes in the set; two remain in private collections.", + "published": true, + "title": "Ramón Casas Sketchbooks", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586574", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/ba35820a-525a-4cfa-8f23-4891c9f798c4", + "id": "ba35820a-525a-4cfa-8f23-4891c9f798c4", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.438228Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "McCormick Library of Special Collections Audiovisual Collection", + "published": true, + "title": "McCormick Library of Special Collections Audiovisual Collection", + "modified_date": "2022-02-17T16:47:28.717491Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.586824", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "id": "8fdc5942-12a0-4abd-8f43-5d19b37ece75", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.884163Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "450b857e-2e74-491e-9ef1-2c73e49f0c16", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": ["photography"], + "visibility": "Public", + "description": "The photographs of George S. Duntley fill 8 boxes and cover the years from approximately 1899-1918. The collection consists of dry plate negatives and record Duntley’s years in Chicago and Evanston while he was attending Northwestern University Medical School. Also included are photographs taken in Bushnell, Illinois, his home town and surrounding areas.", + "published": true, + "title": "George Silas Duntley (1879-1957) Photographs, circa 1899-1918", + "modified_date": "2021-11-10T22:56:44.250402Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.587475", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1245", + "api_link": "[PLACEHOLDER]/450b857e-2e74-491e-9ef1-2c73e49f0c16", + "id": "450b857e-2e74-491e-9ef1-2c73e49f0c16", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.415488Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "f65b3ae2-e4a9-40b7-b0a0-b48416eae767", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Asher Golden Collection contains 29 audiocassette recordings and a small amount of paper documents from Northwestern University’s 1995-1997 football seasons. A sports reporter for Northwestern’s WNUR radio station, Golden covered Wildcat football between 1995 and the January 1, 1997 Citrus Bowl.", + "published": true, + "title": "Asher Golden Collection", + "modified_date": "2022-02-03T21:50:55.433680Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.682714", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/resources/1355", + "api_link": "[PLACEHOLDER]/f65b3ae2-e4a9-40b7-b0a0-b48416eae767", + "id": "f65b3ae2-e4a9-40b7-b0a0-b48416eae767", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.808241Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "fe6ff1c4-5eb9-4a9d-b897-ac50a1fbcb24", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Photo Albums from the collections of Northwestern University Libraries.", + "published": true, + "title": "Photo Albums", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683802", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/fe6ff1c4-5eb9-4a9d-b897-ac50a1fbcb24", + "id": "fe6ff1c4-5eb9-4a9d-b897-ac50a1fbcb24", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.373705Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "d6b795f8-cc01-4373-b2ae-3b7fb8327b9e", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "This collection includes 300 audio recordings of notable WNUR broadcasts. The topics of the recordings range from general aspects of Northwestern University life, student activities and performances, to various social and political speeches and meetings.", + "published": true, + "title": "WNUR Audiotape Recordings", + "modified_date": "2022-02-17T22:40:28.178167Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683858", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/6/archival_objects/509358", + "api_link": "[PLACEHOLDER]/d6b795f8-cc01-4373-b2ae-3b7fb8327b9e", + "id": "d6b795f8-cc01-4373-b2ae-3b7fb8327b9e", + "representative_image": {}, + "create_date": "2022-02-17T20:55:32.416231Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "2933302a-a629-4e3b-81ed-db7a54b33a8c", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Audio recordings of Chicago Chamber Musicians concerts and performances. ", + "published": true, + "title": "Chicago Chamber Musicians Archive, 1989-2010", + "modified_date": "2022-02-07T17:43:00.487626Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.683945", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/2933302a-a629-4e3b-81ed-db7a54b33a8c", + "id": "2933302a-a629-4e3b-81ed-db7a54b33a8c", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.824524Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "6ab4d374-ba2b-4ad3-bc8a-e6613d54c168", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Manuscripts from the collections of Northwestern University Libraries.", + "published": true, + "title": "Manuscripts", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684387", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/6ab4d374-ba2b-4ad3-bc8a-e6613d54c168", + "id": "6ab4d374-ba2b-4ad3-bc8a-e6613d54c168", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.376376Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c3ced30c-3e7c-4762-99bf-52809483885f", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "University Archives Audiovisual Collection", + "published": true, + "title": "University Archives Audiovisual Collection", + "modified_date": "2022-02-18T13:23:30.621596Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684578", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/c3ced30c-3e7c-4762-99bf-52809483885f", + "id": "c3ced30c-3e7c-4762-99bf-52809483885f", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.987430Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "073ad358-d5e9-495b-8cb5-58bd5ec4c189", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Black and white photographs mounted in album from the Herskovits Library of African Studies. ", + "published": true, + "title": "Photographs of Zanzibar", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684859", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/073ad358-d5e9-495b-8cb5-58bd5ec4c189", + "id": "073ad358-d5e9-495b-8cb5-58bd5ec4c189", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.407822Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "d2ae687b-d180-4600-8b1a-886c0cf35e79", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "NU Libraries", + "published": true, + "title": "NU Libraries", + "modified_date": "2022-02-04T18:42:54.078963Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684888", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/d2ae687b-d180-4600-8b1a-886c0cf35e79", + "id": "d2ae687b-d180-4600-8b1a-886c0cf35e79", + "representative_image": {}, + "create_date": "2022-01-21T06:46:05.921501Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c70bcbd0-85c2-41b4-aa44-f184d2521db8", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "Catherine Wolfe Donohue, who contracted radium poisoning while an employee of the Radium Dial Company in Ottawa, Illinois, was part of the class action suit against the company. Damages were awarded to the women in 1938 by the Illinois Industrial Commission. This collection consists of a scrapbook created by the Donohue family with newspaper clippings about the plight of the women and their efforts to get treatment and compensation, some family photographs, publications from Argonne National Laboratory, articles about the case, and a letter from Donohue's cousin, Mary Carroll Cassidy.", + "published": true, + "title": "Catherine Wolfe Donohue Collection on the Radium Dial Company", + "modified_date": "2021-09-28T22:13:35.953690Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684967", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/7/resources/804", + "api_link": "[PLACEHOLDER]/c70bcbd0-85c2-41b4-aa44-f184d2521db8", + "id": "c70bcbd0-85c2-41b4-aa44-f184d2521db8", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.346053Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "8172a9ef-d6df-44a3-ba98-7005af23e230", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Early Automobile Companies Ephemera Collection contains materials dating between 1910 and 1932, and consists of published advertising and marketing brochures, maps, guides and general information about automobiles. As a whole, the collection gives a view of the many automobile companies that thrived at the beginning of the 20th century, as well as a burgeoning travel industry. Primarily centering upon the Midwest and Great Lakes regions, the collection also includes maps and travel guides to the Western and Southern states.", + "published": true, + "title": "Early Automobile Companies Ephemera Collection, 1910-1932", + "modified_date": "2021-09-28T22:15:19.973138Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.684994", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/5/resources/1262", + "api_link": "[PLACEHOLDER]/8172a9ef-d6df-44a3-ba98-7005af23e230", + "id": "8172a9ef-d6df-44a3-ba98-7005af23e230", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.315559Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "5753e30c-608e-4795-b9b2-57e4b1bb27fa", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Visual materials from a regimental history of the 33rd Division which was made up of soldiers from Illinois and was published by the Illinois State Historical Society. The history addresses the organization and training of this Division and where it served in Europe during the 1st World War.", + "published": true, + "title": "The History of the 33rd Division", + "modified_date": "2021-03-17T20:18:35.644274Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.685199", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/5753e30c-608e-4795-b9b2-57e4b1bb27fa", + "id": "5753e30c-608e-4795-b9b2-57e4b1bb27fa", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.368277Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "bd90de9e-8e1e-43c4-8009-dd13a916a2ac", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": ["Evanston"], + "visibility": "Public", + "description": "Postcards with views of Evanston.", + "published": true, + "title": "University Archives Postcards", + "modified_date": "2022-02-24T23:51:15.767070Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764240", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/bd90de9e-8e1e-43c4-8009-dd13a916a2ac", + "id": "bd90de9e-8e1e-43c4-8009-dd13a916a2ac", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/f661aea6-2d6e-4d30-8dc1-45ebc184d99a", + "workId": "8700cad1-8e5e-4734-a86b-071cc7bf39ba" + }, + "create_date": "2021-03-12T02:11:32.435870Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "6f58c85f-f1fc-43c1-be52-678867659ff6", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "The collection depicts the breadth of African experience; documents African life; European life in Africa in all its manifestations; and the African landscape, in particular as it changed over time. Included are photographs showing the building of East Africa’s railways, the growth of its urban centers and the development of European colonial administration. There are outstanding examples of portraiture. The collection provides an unsurpassed resource for the study of the history of photography in East Africa. \n\nCultural Context: Content on this site is drawn from a broad range of historical sources including materials that may contain offensive images or language reflecting the nature of European colonialism in Africa. Such materials should be viewed in the context of the time and place in which they were created. The images and text in this site are presented as specific, original artifacts recording the attitudes, perspectives and beliefs of a different era. Northwestern University does not endorse the views expressed in this collection which may contain images and text offensive to some researchers.", + "published": true, + "title": "The Humphrey Winterton Collection of East African Photographs: 1860-1960", + "modified_date": "2022-02-24T23:51:15.813860Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.764325", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/6f58c85f-f1fc-43c1-be52-678867659ff6", + "id": "6f58c85f-f1fc-43c1-be52-678867659ff6", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/72f0bed1-8222-4671-b3cd-7c491ba0c195", + "workId": "aacb5c5e-76f9-4c23-9f31-10455873d96f" + }, + "create_date": "2021-03-12T02:11:32.362616Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "1d849df1-eb32-43f4-b7b9-e435cff18f7c", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": ["photography"], + "visibility": "Public", + "description": "These images of African daily life date from the mid-20th century. Photographs come from the following countries and territories: Basutoland, Belgian Congo, Dahomey, French Equatorial Africa, French Guinea, French Togoland, Ghana, Ivory Coast, Kenya, Liberia, Mauritania, Niger (French), Nigeria, Northern Rhodesia, Ruanda-Urundi, Senegal, Sierra Leone, South Africa, Southern Rhodesia, Southwest Africa, Tanganyika, and Tunisia.", + "published": true, + "title": "Vernon McKay Photographs", + "modified_date": "2022-02-24T23:51:16.488587Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.845465", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/4/resources/296", + "api_link": "[PLACEHOLDER]/1d849df1-eb32-43f4-b7b9-e435cff18f7c", + "id": "1d849df1-eb32-43f4-b7b9-e435cff18f7c", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/a70f184d-3229-4200-9140-d5c74c85c30d", + "workId": "a44ead3a-a622-4a7b-aecb-496a41481896" + }, + "create_date": "2021-03-12T02:11:32.429905Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "c0c279a9-c2a9-4f9f-a111-a2d4e2f78658", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "Images from the publication : Une cité moderne : dessins / de Rob Mallet-Stevens ; preface de Frantz Jourdain. Author: Mallet-Stevens, Robert, 1886-1945. Format: 1 portfolio (3, 32 leaves of plates : chiefly col. ill.) ; 34 cm.. Language: French; English Notes: Text in French; captions in French and English.", + "published": true, + "title": "Une cité moderne : dessins, by Rob Mallet-Stevens", + "modified_date": "2022-02-24T23:51:16.520330Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:14.845518", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/c0c279a9-c2a9-4f9f-a111-a2d4e2f78658", + "id": "c0c279a9-c2a9-4f9f-a111-a2d4e2f78658", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b515c075-3769-4245-b678-a26867f2123e", + "workId": "a0ba6462-197e-4e91-8d04-50617a3d11f1" + }, + "create_date": "2021-03-12T02:11:32.425485Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "4e9ed062-fc73-40ba-9514-2537eeae4c38", + "_score": 0.3395071, + "_source": { + "featured": null, + "keywords": [], + "visibility": "Public", + "description": "La Caricature was a satirical weekly published French periodical that was distributed in Paris between 1830 and 1843 during the July Monarchy. This collection contains works from when Auguste Audibert was editor. The complete volumes of La Caricature can be found in HathiTrust Digital Library. These are individual plates only. Related URL connects to the complete volume. ", + "published": true, + "title": "La Caricature (1880-1893, 1899; Albert Robida, founding editor)", + "modified_date": "2022-02-24T23:51:16.545159Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.613663", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/4e9ed062-fc73-40ba-9514-2537eeae4c38", + "id": "4e9ed062-fc73-40ba-9514-2537eeae4c38", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/941d3942-4afe-481b-86ed-56b631076b70", + "workId": "e67a3f10-b1b9-414a-91df-0e7d8aff9253" + }, + "create_date": "2021-03-12T02:11:32.397582Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "0ba3a256-4952-44ea-b0d9-0772835ff137", + "_score": 0.3395071, + "_source": { + "featured": false, + "keywords": [""], + "visibility": "Public", + "description": "The Ronald J. Sullivan Photograph Collection is an extensive collection of photographs and slides of public and commercial buses and public transit and commuter trains, dating from 1946-2000. The bulk of the collection comprises photographs taken by Ronald J. Sullivan, an amateur photographer and rail and bus enthusiast; also included are historic images of Chicago-area transit that were part of Sullivan’s personal collection, and which supplement the photographer’s original material. The majority of the photographs were taken in Chicago, Illinois and the surrounding area, with a few in other cities and international locations. Sullivan documented nearly all of the photographs and slides in this collection in detail with bus or train numbers, route numbers, street intersections or stations, and dates. For the sake of consistency and ease of searching for specific items the City of Chicago Street Guide abbreviations and a list of common abbreviations found in the collection will be used. ", + "published": true, + "title": "Ronald J. Sullivan Photograph Collection", + "modified_date": "2022-02-24T23:51:16.574977Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.613770", + "api_model": "Collection", + "finding_aid_url": "https://findingaids.library.northwestern.edu/repositories/5/resources/1290", + "api_link": "[PLACEHOLDER]/0ba3a256-4952-44ea-b0d9-0772835ff137", + "id": "0ba3a256-4952-44ea-b0d9-0772835ff137", + "representative_image": { + "url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/5f8f3ecb-d8fc-4b75-80a3-08efc047a9c7", + "workId": "ef5f341f-50cc-4ff3-a84c-7f13ac0d1afa" + }, + "create_date": "2021-03-12T02:11:32.365146Z" + } + }, + { + "_index": "dc-v2-collection-1658438321581379", + "_type": "_doc", + "_id": "18ec4c6b-192a-4ab8-9903-ea0f393c35f7", + "_score": 0.3395071, + "_source": { + "featured": true, + "keywords": ["featured"], + "visibility": "Public", + "description": "The Berkeley Folk Music Festival Archive consists of roughly 33,500 items, including photographs of folk artists and groups, posters, flyers, press clippings, correspondence, publicity information, audio, video, and more about the Festival and the folk music revival as a whole. Audio recordings from the Archive can be found by copying and pasting http://libraries.nu/FolkFestivalAudio into your browser. A digital exhibit, The Berkeley Folk Music Festival & the Folk Revival on the US West Coast—An Introduction, can be found at https://sites.northwestern.edu/bfmf. We welcome your input and comments to help us continue to improve the accuracy of this rich, wide-ranging collection of materials at https://sites.northwestern.edu/bfmf/contact-us/.", + "published": true, + "title": "Berkeley Folk Music Festival", + "modified_date": "2022-07-12T21:14:30.429839Z", + "admin_email": null, + "indexed_at": "2022-08-24T17:29:15.615008", + "api_model": "Collection", + "finding_aid_url": null, + "api_link": "[PLACEHOLDER]/18ec4c6b-192a-4ab8-9903-ea0f393c35f7", + "id": "18ec4c6b-192a-4ab8-9903-ea0f393c35f7", + "representative_image": {}, + "create_date": "2021-03-12T02:11:32.456787Z" + } + } + ] + } +} diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js new file mode 100644 index 00000000..2e83a0b8 --- /dev/null +++ b/test/integration/get-collections.test.js @@ -0,0 +1,46 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const getCollectionsHandler = require("../../src/handlers/get-collections"); +const RequestPipeline = require("../../src/api/request/pipeline"); + +describe("Collections route", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /collections", () => { + const handler = getCollectionsHandler.handler; + const originalQuery = { size: 10, from: 0 }; + const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); + const searchToken = + "N4IgRg9gJgniBcoDOBLAXgUwQRgAwF8AaEAW2gwBskEBtEAYwgoo3oBcUIA7agXXyA"; + + it("requires a valid searchToken", async () => { + const event = helpers + .mockEvent("GET", "/collections") + .pathPrefix("/api/v2") + .queryParams({ searchToken: "Ceci n'est pas une searchToken" }) + .render(); + + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("searchToken is invalid"); + }); + + it("paginates results using a searchToken and page number", async () => { + mock + .post("/dc-v2-collection/_search", authQuery) + .reply(200, helpers.testFixture("mocks/collections.json")); + const event = helpers + .mockEvent("GET", "/collections") + .pathPrefix("/api/v2") + .queryParams({ searchToken, page: 1 }) + .body(authQuery) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + }); + }); +}); diff --git a/test/unit/api/pagination.test.js b/test/unit/api/pagination.test.js index 1a7bb582..bf5313d9 100644 --- a/test/unit/api/pagination.test.js +++ b/test/unit/api/pagination.test.js @@ -18,6 +18,7 @@ describe("Paginator", function () { this.beforeEach(() => { pager = new Paginator( "http://dcapi.library.northwestern.edu/v2/", + "search", ["works"], requestBody ); diff --git a/test/unit/api/response/opensearch.test.js b/test/unit/api/response/opensearch.test.js index 6df5e7d0..a360a427 100644 --- a/test/unit/api/response/opensearch.test.js +++ b/test/unit/api/response/opensearch.test.js @@ -10,6 +10,7 @@ describe("OpenSearch response transformer", () => { beforeEach(() => { pager = new Paginator( "http://dcapi.library.northwestern.edu/v2/", + "search", ["works"], { query: { match_all: {} } } ); From 4f739e96520a030816eca20a43614c423ac454ee Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 29 Aug 2022 21:51:34 +0000 Subject: [PATCH 16/61] Add documentation Integrate mkdocs and swagger-ui Move docs to /docs/v2 Exclude docs from coverage --- .github/workflows/build.yml | 4 +- .github/workflows/docs.yml | 64 +++++ .gitignore | 5 + docs/docs/css/fonts.css | 83 ++++++ docs/docs/css/overrides.css | 3 + docs/docs/index.md | 7 + docs/docs/spec/openapi.html | 28 ++ docs/docs/spec/openapi.yaml | 157 ++++++++++++ docs/docs/spec/types.yaml | 69 +++++ docs/mkdocs.yml | 28 ++ docs/poetry.lock | 497 ++++++++++++++++++++++++++++++++++++ docs/pyproject.toml | 19 ++ nyc.config.js | 2 +- template.yaml | 52 ++++ 14 files changed, 1015 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/docs/css/fonts.css create mode 100644 docs/docs/css/overrides.css create mode 100644 docs/docs/index.md create mode 100644 docs/docs/spec/openapi.html create mode 100644 docs/docs/spec/openapi.yaml create mode 100644 docs/docs/spec/types.yaml create mode 100644 docs/mkdocs.yml create mode 100644 docs/poetry.lock create mode 100644 docs/pyproject.toml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26ee1f64..d4668e08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ -name: Digital Collections API +name: Run Tests on: - push jobs: - build: + test: env: AWS_ACCESS_KEY_ID: ci AWS_SECRET_ACCESS_KEY: ci diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..d6651338 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,64 @@ +name: Generate Docs +on: + push: + branches: + - deploy/staging + - main +jobs: + docs-changed: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.changed-files.outputs.any_modified == 'true' }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Get changed doc files + id: changed-files + uses: tj-actions/changed-files@v29.0.2 + with: + files: | + .github/workflows/docs.yaml + docs/* + publish-docs: + needs: docs-changed + if: ${{ needs.docs-changed.outputs.result == 'true' }} + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: + name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@master + with: + role-to-assume: arn:aws:iam::${{ secrets.AwsAccount }}:role/github-actions-role + aws-region: us-east-1 + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: abatilo/actions-poetry@v2 + with: + poetry-version: 1.1.14 + - name: Install dependencies + run: poetry install + working-directory: ./docs + - name: Build docs + run: poetry run mkdocs build --clean + working-directory: ./docs + - name: Determine correct deploy domain for environment + run: sed -i s/rdc\\.library\\.northwestern\\.edu/${ZONE}/g docs/site/spec/openapi.* + env: + ZONE: ${{ secrets.HostedZone }} + - name: Generate JSON API + uses: openapi-generators/openapitools-generator-action@v1 + with: + generator: openapi + openapi-file: docs/site/spec/openapi.yaml + command-args: -o docs/site/spec + - name: Copy to S3 + run: aws s3 sync --delete docs/site/ s3://dcapi-docs.${ZONE}/ + env: + ZONE: ${{ secrets.HostedZone }} diff --git a/.gitignore b/.gitignore index cb1d4d08..0275d0cf 100644 --- a/.gitignore +++ b/.gitignore @@ -205,6 +205,11 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +/docs/docs/spec/.openapi-* +/docs/docs/spec/openapi.json +/docs/site + +.vscode samconfig.toml env.json diff --git a/docs/docs/css/fonts.css b/docs/docs/css/fonts.css new file mode 100644 index 00000000..0a834ef4 --- /dev/null +++ b/docs/docs/css/fonts.css @@ -0,0 +1,83 @@ +@font-face { + font-family: "Akkurat Pro Light"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProLight.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Akkurat Pro Light Italic"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProLightItalic.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Akkurat Pro Regular"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProRegular.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Akkurat Pro Italic"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProItalic.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Akkurat Pro Bold"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProBold.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Akkurat Pro Bold Italic"; + src: url("https://common.northwestern.edu/v8/css/fonts/AkkuratProBoldItalic.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Campton Book"; + src: url("https://common.northwestern.edu/v8/css/fonts/CamptonBook.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Campton Bold"; + src: url("https://common.northwestern.edu/v8/css/fonts/CamptonBold.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Campton Extra Bold"; + src: url("https://common.northwestern.edu/v8/css/fonts/CamptonExtraBold.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Campton Extra Light"; + src: url("https://common.northwestern.edu/v8/css/fonts/CamptonExtraLight.woff") format("woff"); + font-weight: normal; + font-style:normal +} + +@font-face { + font-family: "Noto Serif Italic"; + font-style: italic; + font-weight: 400; + src: url("https://common.northwestern.edu/v8/css/fonts/noto-serif-v16-latin-italic.woff") format("woff") +} + +@font-face { + font-family: "Noto Serif Bold Italic"; + font-style: italic; + font-weight: 700; + src: url("https://common.northwestern.edu/v8/css/fonts/noto-serif-v16-latin-700italic.woff") format("woff") +} \ No newline at end of file diff --git a/docs/docs/css/overrides.css b/docs/docs/css/overrides.css new file mode 100644 index 00000000..8ab993ef --- /dev/null +++ b/docs/docs/css/overrides.css @@ -0,0 +1,3 @@ +body { + font-family: "Akkurat Pro Regular", -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif +} \ No newline at end of file diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 00000000..4c2e2179 --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,7 @@ +## Overview + +Here are the docs. + +[Here](./spec/openapi.json) is the OpenAPI spec. + +[Here](./spec/openapi.html) is the Swagger UI documentation. \ No newline at end of file diff --git a/docs/docs/spec/openapi.html b/docs/docs/spec/openapi.html new file mode 100644 index 00000000..4ab7c82f --- /dev/null +++ b/docs/docs/spec/openapi.html @@ -0,0 +1,28 @@ + + + + + + + SwaggerUI + + + +
+ + + + + diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml new file mode 100644 index 00000000..a87da77a --- /dev/null +++ b/docs/docs/spec/openapi.yaml @@ -0,0 +1,157 @@ +openapi: 3.0.1 +info: + title: Northwestern University Libraries Digital Collections API + description: > + This is full Markdown intro text. This API drives the [Northwestern University Libraries Digital + Collections](https://dc.library.northwestern.edu) site. + + I can even put line breaks in it. I can also link back to the [main documentation](..). + version: 2.0.0 + contact: + name: NUL Repository Team + email: repository@northwestern.edu +servers: +- url: https://dcapi.rdc.library.northwestern.edu/api/v2 +tags: + - name: Models + description: > + Endpoints for listing and retrieving Collections, FileSets, and Works. + - name: Search + description: > + Endpoints for searching the index. +paths: + /collections: + get: + operationId: getCollections + tags: + - Collection + parameters: + - name: page + in: query + required: false + description: Page number of results to retrieve + schema: + type: integer + minimum: 1 + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" + /collections/{id}: + get: + tags: + - Collection + operationId: getCollectionById + parameters: + - $ref: "./types.yaml#/components/parameters/id" + responses: + 200: + $ref: "./types.yaml#/components/responses/DocumentResponse" + /file-sets/{id}: + get: + operationId: getFileSetById + tags: + - FileSet + parameters: + - $ref: "./types.yaml#/components/parameters/id" + responses: + 200: + $ref: "./types.yaml#/components/responses/DocumentResponse" + /works/{id}: + get: + operationId: getWorkById + tags: + - Work + parameters: + - $ref: "./types.yaml#/components/parameters/id" + responses: + 200: + $ref: "./types.yaml#/components/responses/DocumentResponse" + /works/{id}/thumbnail: + get: + operationId: getWorkThumbnail + tags: + - Work + parameters: + - $ref: "./types.yaml#/components/parameters/id" + - name: aspect + in: query + required: false + description: Desired aspect ratio + schema: + type: string + enum: + - full + - square + - name: size + in: query + required: false + description: Size of largest dimension + schema: + type: integer + minimum: 1 + maximum: 300 + responses: + 200: + description: A thumbnail image for the given work + content: + image/jpeg: + schema: + type: string + format: binary + /search: + get: + operationId: getSearch + tags: + - Search + parameters: + - name: searchToken + in: query + required: true + description: Search token from previous search response + schema: + type: string + - name: page + in: query + required: false + description: Page number of results to retrieve + schema: + type: integer + minimum: 1 + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" + post: + operationId: postSearch + tags: + - Search + requestBody: + content: + application/json: + schema: + type: object + + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" + /search/{models}: + post: + operationId: postSearchWithModels + tags: + - Search + parameters: + - name: models + in: path + description: Comma-delimited list of models to search + required: true + schema: + type: array + items: + type: string + enum: + - collections + - file-sets + - Works + style: simple + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" diff --git a/docs/docs/spec/types.yaml b/docs/docs/spec/types.yaml new file mode 100644 index 00000000..eaffd83a --- /dev/null +++ b/docs/docs/spec/types.yaml @@ -0,0 +1,69 @@ +openapi: 3.0.1 +components: + parameters: + id: + name: id + in: path + required: true + description: Collection, FileSet, or Work ID + schema: + type: string + format: uuid + responses: + DocumentResponse: + description: A single document response + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/IndexDocument" + info: + type: object + SearchResponse: + description: A page of search results + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/IndexDocument" + description: An array of response documents + pagination: + $ref: "#/components/schemas/PaginationInfo" + schemas: + IndexDocument: + type: object + description: A single index document + PaginationInfo: + type: object + description: Pagination info for the current response + properties: + next_url: + type: string + description: Full URL to the next page of results + prev_url: + type: string + description: Full URL to the previous page of results + query_url: + type: string + description: Base URL to repeat this query for a given page + current_page: + type: integer + description: Index of current page of results + limit: + type: integer + description: Number of results per page + offset: + type: integer + description: Starting index of first result on the current page + total_hits: + type: integer + description: Total number of results + total_pages: + type: integer + description: Total number of result pages diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..3c87ebc7 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,28 @@ +site_name: "NUL Digital Collections API" +site_url: https://dcapi.rdc.library.northwestern.edu/api/v2/ +use_directory_urls: true +theme: + name: material +extra_css: +- css/fonts.css +- css/overrides.css +repo_name: "nulib/dc-api-v2" +repo_url: "https://github.com/nulib/dc-api-v2" +edit_uri: blob/main/docs/docs/ +plugins: + - macros + - search +markdown_extensions: + - admonition #adds ability to have custom highlight boxes with !!! + - codehilite: + guess_lang: false + - def_list + - footnotes + - smarty + - pymdownx.details + - pymdownx.inlinehilite + - pymdownx.mark + - pymdownx.superfences + - pymdownx.tilde + - toc: + permalink: true diff --git a/docs/poetry.lock b/docs/poetry.lock new file mode 100644 index 00000000..a3fe5ac7 --- /dev/null +++ b/docs/poetry.lock @@ -0,0 +1,497 @@ +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.5" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "diagrams" +version = "0.21.1" +description = "Diagram as Code" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +graphviz = ">=0.13.2,<0.20.0" +jinja2 = ">=2.10,<4.0" + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["wheel", "flake8", "markdown", "twine"] + +[[package]] +name = "graphviz" +version = "0.19.2" +description = "Simple Python interface for Graphviz" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +dev = ["tox (>=3)", "flake8", "pep8-naming", "wheel", "twine"] +docs = ["sphinx (>=1.8)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["pytest (>=6)", "pytest-mock (>=3)", "mock (>=4)", "pytest-cov", "coverage"] + +[[package]] +name = "importlib-metadata" +version = "4.12.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.3.7" +description = "Python implementation of Markdown." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mkdocs" +version = "1.3.1" +description = "Project documentation with Markdown." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=4.3" +Jinja2 = ">=2.10.2" +Markdown = ">=3.2.1,<3.4" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +PyYAML = ">=3.10" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] + +[[package]] +name = "mkdocs-macros-plugin" +version = "0.5.12" +description = "" +category = "main" +optional = false +python-versions = ">=3.5" +develop = false + +[package.dependencies] +jinja2 = "*" +mkdocs = ">=0.17" +python-dateutil = "*" +pyyaml = "*" +termcolor = "*" + +[package.extras] +test = ["mkdocs-macros-test", "mkdocs-material (>=6.2)", "mkdocs-include-markdown-plugin"] + +[package.source] +type = "git" +url = "https://github.com/fralau/mkdocs_macros_plugin.git" +reference = "v0.5.12" +resolved_reference = "4bf847b3460471ee9cf2559cd5562954f378fe3b" + +[[package]] +name = "mkdocs-material" +version = "8.4.2" +description = "Documentation that simply works" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +jinja2 = ">=3.0.2" +markdown = ">=3.2" +mkdocs = ">=1.3.0" +mkdocs-material-extensions = ">=1.0.3" +pygments = ">=2.12" +pymdown-extensions = ">=9.4" + +[[package]] +name = "mkdocs-material-extensions" +version = "1.0.3" +description = "Extension pack for Python Markdown." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pymdown-extensions" +version = "9.5" +description = "Extension pack for Python Markdown." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +markdown = ">=3.2" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "termcolor" +version = "1.1.0" +description = "ANSII Color formatting for output in terminal." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "watchdog" +version = "2.1.9" +description = "Filesystem events monitoring" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "zipp" +version = "3.8.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "19c6631c5ea5f1b10be8c0d9c93637d1b6930d4c437196825d483dcb1474dd09" + +[metadata.files] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +diagrams = [ + {file = "diagrams-0.21.1-py3-none-any.whl", hash = "sha256:1d0fea187ed76136dadccf943da914dd9974ebdd3cbb993f4283ec8f51cf90f2"}, + {file = "diagrams-0.21.1.tar.gz", hash = "sha256:732caa4e61113a06b9a80965be6171c4c5858f3570b5b1f5ba52304a6eeade60"}, +] +ghp-import = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] +graphviz = [ + {file = "graphviz-0.19.2-py3-none-any.whl", hash = "sha256:c0a6ac9fadaa308c4ab0d526749c6e127425ec5a76644bbb03e2e04b19a9da9f"}, + {file = "graphviz-0.19.2.zip", hash = "sha256:7c90cebc147c18bcdffcd3c76db58cbface5d45fe0247a2f3bfb144d32a8c77c"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, + {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, +] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +markdown = [ + {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, + {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] +mkdocs = [ + {file = "mkdocs-1.3.1-py3-none-any.whl", hash = "sha256:fda92466393127d2da830bc6edc3a625a14b436316d1caf347690648e774c4f0"}, + {file = "mkdocs-1.3.1.tar.gz", hash = "sha256:a41a2ff25ce3bbacc953f9844ba07d106233cd76c88bac1f59cb1564ac0d87ed"}, +] +mkdocs-macros-plugin = [] +mkdocs-material = [ + {file = "mkdocs-material-8.4.2.tar.gz", hash = "sha256:704c64c3fff126a3923c2961d95f26b19be621342a6a4e49ed039f0bb7a5c540"}, + {file = "mkdocs_material-8.4.2-py2.py3-none-any.whl", hash = "sha256:166287bb0e4197804906bf0389a852d5ced43182c30127ac8b48a4e497ecd7e5"}, +] +mkdocs-material-extensions = [ + {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, + {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] +pymdown-extensions = [ + {file = "pymdown_extensions-9.5-py3-none-any.whl", hash = "sha256:ec141c0f4983755349f0c8710416348d1a13753976c028186ed14f190c8061c4"}, + {file = "pymdown_extensions-9.5.tar.gz", hash = "sha256:3ef2d998c0d5fa7eb09291926d90d69391283561cf6306f85cd588a5eb5befa0"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +termcolor = [ + {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, +] +watchdog = [ + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, + {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, + {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, + {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, + {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, + {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, + {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, + {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, + {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, + {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, +] +zipp = [ + {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, + {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, +] diff --git a/docs/pyproject.toml b/docs/pyproject.toml new file mode 100644 index 00000000..e49aef63 --- /dev/null +++ b/docs/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "dc-api-v2" +version = "2.0.0" +description = "Northwestern University Library Digital Collections API Documentation" +authors = ["Michael B. Klein "] + +[tool.poetry.dependencies] +python = "^3.8" +mkdocs = "^1.1.2" +mkdocs-macros-plugin = { git = "https://github.com/fralau/mkdocs_macros_plugin.git", tag = "v0.5.12" } +Pygments = "^2.7.3" +diagrams = "^0.21.1" +mkdocs-material = "^8.4.2" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/nyc.config.js b/nyc.config.js index bff6f96b..cfd60f0d 100644 --- a/nyc.config.js +++ b/nyc.config.js @@ -1,7 +1,7 @@ 'use strict'; const defaultExclude = require('@istanbuljs/schema/default-exclude'); -const localExclude = [".aws-sam/**/*"]; +const localExclude = [".aws-sam/**/*", "docs/**/*"]; module.exports = { all: true, "check-coverage": true, diff --git a/template.yaml b/template.yaml index 75541745..42b681b7 100644 --- a/template.yaml +++ b/template.yaml @@ -38,6 +38,7 @@ Parameters: Description: Stage name of the v1 API to mount on /api/v1 Default: latest Resources: + # V2 API getCollectionsFunction: Type: AWS::Serverless::Function Properties: @@ -270,6 +271,8 @@ Resources: CertificateArn: !Ref CustomDomainCertificateArn Route53: HostedZoneName: !Sub "${CustomDomainZone}." + + # v1 API Mapping v1Mapping: Type: AWS::ApiGatewayV2::ApiMapping Properties: @@ -277,3 +280,52 @@ Resources: DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" ApiId: !Ref V1ApiId Stage: !Ref V1ApiStage + + # Documentation + docsApi: + Type: AWS::Serverless::HttpApi + Properties: + StageName: v2 + docsMapping: + Type: AWS::ApiGatewayV2::ApiMapping + Properties: + ApiMappingKey: docs/v2 + DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" + ApiId: !Ref docsApi + Stage: !Ref docsApiv2Stage + docsBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Sub "dcapi-docs.${CustomDomainZone}" + AccessControl: PublicRead + WebsiteConfiguration: + IndexDocument: index.html + ErrorDocument: index.html + docsBucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + PolicyDocument: + Id: MyPolicy + Version: 2012-10-17 + Statement: + - Sid: PublicReadForGetBucketObjects + Effect: Allow + Principal: "*" + Action: 's3:GetObject' + Resource: !Sub "arn:aws:s3:::${docsBucket}/*" + Bucket: !Ref docsBucket + docsIntegration: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref docsApi + IntegrationMethod: GET + IntegrationType: HTTP_PROXY + IntegrationUri: !Sub "http://${docsBucket}.s3-website-us-east-1.amazonaws.com/{proxy}" + PayloadFormatVersion: "1.0" + docsRoute: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref docsApi + AuthorizationType: NONE + RouteKey: GET /{proxy+} + Target: !Sub "integrations/${docsIntegration}" From 8ea2a3dc906d5d3b3df28b26fee8c2b455a6c8da Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Fri, 2 Sep 2022 17:25:18 +0000 Subject: [PATCH 17/61] Update README with instructions on running in dev mode --- .gitignore | 8 +++--- README.md | 63 +++++++++++++++++++++++++++++++++++++++------- dev/env.json | 7 ++++++ dev/samconfig.toml | 7 ++++++ 4 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 dev/env.json create mode 100644 dev/samconfig.toml diff --git a/.gitignore b/.gitignore index 0275d0cf..ba72ff82 100644 --- a/.gitignore +++ b/.gitignore @@ -205,12 +205,12 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam + /docs/docs/spec/.openapi-* /docs/docs/spec/openapi.json /docs/site .vscode -samconfig.toml -env.json - -# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +/samconfig.toml +/env.json diff --git a/README.md b/README.md index 3146c9fe..af47d1d2 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,58 @@ ## Directory structure ``` -src/ - api/ - code that directly supports API requests - request/ - code to wrap/transform/modify incoming queries - response/ - code to transform OpenSearch responses into the proper result format - iiif/ - iiif formatted response transformers - opensearch/ - opensearch formatted response transformers - oai-pmh/ - oai-pmh formatted response transformers - aws/ - lower-level code to interact with AWS resources and OpenSearch - handlers/ - minimal code required to bridge between API Gateway request and core logic +. +├── docs/ - mkdocs-based API documentation +├── events/ - sample HTTP API Lambda events +├── src/ +│ └── api/ - code that directly supports API requests +│ ├── request/ - code to wrap/transform/modify incoming queries +│ ├── response/ - code to transform OpenSearch responses into the proper result format +│ │ ├── iiif/ - iiif formatted response transformers +│ │ ├── opensearch/ - opensearch formatted response transformers +│ │ └── oai-pmh/ - oai-pmh formatted response transformers +│ ├── aws/ - lower-level code to interact with AWS resources and OpenSearch +│ └── handlers/ - minimal code required to bridge between API Gateway request and core logic +└── test/ - tests and test helpers ``` + +## Local development setup + +### `samconfig.toml` + +The configuration file that tells the `sam` command how to run and deploy the API is called `samconfig.toml`. There are two ways to get that file. From the local project root: + +1. For local development only: `ln -s dev/samconfig.toml .` +2. If you need to be able to deploy: `ln -s /path/to/tfvars/dc-api/samconfig.toml .` (You will need to have a local working copy of the private `tfvars` repo first.) + +Whichever you choose, the resulting file will be ignored by `git`. + +### `env.json` + +The `env.json` file contains environment variable values for the lambda functions defined in the API for use in local development. An initial (empty) version with all the names of the necessary variables is in `dev/env.json`. Copy (**do not symlink**) this file into the project root and customize it for your own environment. It will also be ignored by `git`. + +Some of the values can be found as follows: + +- `API_TOKEN_SECRET` - already defined; value has to exist but doesn't matter in dev mode +- `ELASTICSEARCH_ENDPOINT` - run the following command: + ``` + aws secretsmanager get-secret-value \ + --secret-id dev-environment/config/meadow --query SecretString \ + --output text | jq -r '.index.index_endpoint | sub("^https?://";"")' + ``` +- `ENV_PREFIX` - The username and environment indicating which index to use. Usually your dev environment user prefix followed by `-dev` (e.g., `mbk-dev`), but if you want to use your test index or someone else's index, adjust the value accordingly. + +## Running the API locally + +To start the API in development mode, run the following commands: + +``` +rm -rf .aws-sam +sam local start-api --host 0.0.0.0 --log-file dc-api.log +``` + +The API will be available at: + +- `http://localhost:3000` (from your dev environment) +- `http://USER_PREFIX.dev.library.northwestern.edu:3000` (from elsewhere) + - Don't forget to [open port 3000](https://github.com/nulib/aws-developer-environment#convenience-scripts) if you want to access it remotely diff --git a/dev/env.json b/dev/env.json new file mode 100644 index 00000000..b013ddd7 --- /dev/null +++ b/dev/env.json @@ -0,0 +1,7 @@ +{ + "Parameters": { + "API_TOKEN_SECRET": "DEVELOPMENT_SECRET", + "ELASTICSEARCH_ENDPOINT": "", + "ENV_PREFIX": "" + } +} diff --git a/dev/samconfig.toml b/dev/samconfig.toml new file mode 100644 index 00000000..00efd7e8 --- /dev/null +++ b/dev/samconfig.toml @@ -0,0 +1,7 @@ +version = 0.1 +[default] +[default.deploy] +[default.deploy.parameters] + +[default.local_start_api.parameters] +env_vars = "env.json" From 0a9c7530f0c9272400e41cd6649f5ee893864c85 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Wed, 10 Aug 2022 19:02:07 +0000 Subject: [PATCH 18/61] IIIF Collection search handler --- dev/env.json | 5 +- src/api/pagination.js | 12 +- src/api/request/models.js | 11 +- src/api/response/iiif/collection.js | 121 +++++++++++++ src/api/response/opensearch/index.js | 10 +- src/api/response/transformer.js | 30 ++++ src/handlers/search.js | 159 ++++++++++++++---- template.yaml | 8 + test/integration/search.test.js | 44 ++++- .../unit/api/response/iiif/collection.test.js | 51 ++++++ 10 files changed, 408 insertions(+), 43 deletions(-) create mode 100644 src/api/response/iiif/collection.js create mode 100644 src/api/response/transformer.js create mode 100644 test/unit/api/response/iiif/collection.test.js diff --git a/dev/env.json b/dev/env.json index b013ddd7..ffcaadf5 100644 --- a/dev/env.json +++ b/dev/env.json @@ -2,6 +2,7 @@ "Parameters": { "API_TOKEN_SECRET": "DEVELOPMENT_SECRET", "ELASTICSEARCH_ENDPOINT": "", - "ENV_PREFIX": "" + "ENV_PREFIX": "", + "DC_URL": "" } -} +} \ No newline at end of file diff --git a/src/api/pagination.js b/src/api/pagination.js index 4ce71801..c348aaab 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -9,8 +9,8 @@ async function decodeSearchToken(token) { return JSON.parse(await decompress(token)); } -async function encodeSearchToken(models, body) { - let token = { body: { size: 10 }, models }; +async function encodeSearchToken(models, body, format, options) { + let token = { body: { size: 10 }, models, format, options }; for (const field in body) { if (encodeFields.includes(field)) { token.body[field] = body[field]; @@ -44,18 +44,20 @@ function thisPage(body) { } class Paginator { - constructor(baseUrl, route, models, body) { + constructor(baseUrl, route, models, body, format, options) { this.baseUrl = baseUrl; this.route = route; this.models = models; this.body = { ...body }; + this.format = format; + this.options = options; } async pageInfo(count) { let url = new URL(this.route, this.baseUrl); url.searchParams.set( "searchToken", - await encodeSearchToken(this.models, this.body) + await encodeSearchToken(this.models, this.body, this.format, this.options) ); const prev = prevPage(this.body, count); @@ -68,6 +70,8 @@ class Paginator { offset: from(this.body), total_hits: count, total_pages: maxPage(this.body, count), + options: this.options, + format: this.format, }; if (prev) { url.searchParams.set("page", prev); diff --git a/src/api/request/models.js b/src/api/request/models.js index 96692475..e2a91e73 100644 --- a/src/api/request/models.js +++ b/src/api/request/models.js @@ -6,7 +6,14 @@ const mapTargets = { collections: "dc-v2-collection", }; -function validModels(models) { +function extractRequestedModels(requestedModels) { + return requestedModels == null ? ["works"] : requestedModels.split(","); +} + +function validModels(models, format) { + if (format === "iiif") { + return models.length == 1 && models.every((model) => model === "works"); + } return models.every(isAllowed); } @@ -18,4 +25,4 @@ function modelsToTargets(models) { return String(models.map((model) => prefix(mapTargets[model]))); } -module.exports = { modelsToTargets, validModels }; +module.exports = { extractRequestedModels, modelsToTargets, validModels }; diff --git a/src/api/response/iiif/collection.js b/src/api/response/iiif/collection.js new file mode 100644 index 00000000..827e93b5 --- /dev/null +++ b/src/api/response/iiif/collection.js @@ -0,0 +1,121 @@ +const dcUrl = process.env.DC_URL || "http://placeholder-for-dc-url"; + +async function transform(response, pager) { + if (response.statusCode === 200) { + const responseBody = JSON.parse(response.body); + const pageInfo = await pager.pageInfo(responseBody.hits.total.value); + + return { + statusCode: 200, + body: JSON.stringify(await buildCollection(responseBody, pageInfo)), + }; + } + return transformError(response); +} + +async function buildCollection(responseBody, pageInfo) { + const collectionLabel = pageInfo.options.collectionLabel; + const collectionSummary = pageInfo.options.collectionSummary; + + return { + "@context": ["http://iiif.io/api/presentation/3/context.json"], + id: collectionId(pageInfo), + type: "Collection", + label: { + none: [`${collectionLabel}`], + }, + summary: { + none: [`${collectionSummary}`], + }, + homepage: [ + { + id: homepageUrl(pageInfo), + type: "Text", + format: "text/html", + label: { + none: [`Results for ${collectionLabel} of ${collectionSummary}`], + }, + }, + ], + items: getItems(responseBody?.hits?.hits, pageInfo), + }; +} + +function getItems(hits, pageInfo) { + const items = hits.map((item) => loadItem(item["_source"])); + + if (pageInfo?.next_url) { + items.push({ + id: pageInfo.next_url, + type: "Collection", + label: { + none: ["Next page"], + }, + }); + } + + return items; +} + +function collectionId(pageInfo) { + let collectionId = new URL(pageInfo.query_url); + if (pageInfo.current_page > 1) { + collectionId.searchParams.set("page", pageInfo.current_page); + } + return collectionId; +} + +function homepageUrl(pageInfo) { + const result = new URL("/search", dcUrl); + result.searchParams.set("q", pageInfo.options.queryString); + return result; +} + +function loadItem(item) { + return { + id: item.iiif_manifest, + type: "Manifest", + homepage: [ + { + id: `${dcUrl}/items/${item.id}`, + type: "Text", + format: "text/html", + label: { + none: [`${item.title}`], + }, + }, + ], + label: { + none: [`${item.title}`], + }, + summary: { + none: [`${item.work_type}`], + }, + thumbnail: [ + { + id: [`${item.representative_file_set.url}/full/400,/0/default.jpg`], + service: [ + { + profile: "http://iiif.io/api/image/2/level2.json", + "@context": "http://iiif.io/api/image/2/context.json", + "@id": `${item.representative_file_set.url}`, + }, + ], + type: "Image", + width: 400, + height: 400, + }, + ], + }; +} + +function transformError(response) { + const responseBody = { status: response.statusCode, error: "TODO" }; + + return { + statusCode: response.statusCode, + body: JSON.stringify(responseBody), + }; +} + +module.exports = { transform }; diff --git a/src/api/response/opensearch/index.js b/src/api/response/opensearch/index.js index c1222c28..437b082b 100644 --- a/src/api/response/opensearch/index.js +++ b/src/api/response/opensearch/index.js @@ -20,13 +20,21 @@ async function transformMany(responseBody, pager) { statusCode: 200, body: JSON.stringify({ data: extractSource(responseBody.hits.hits), - pagination: await pager.pageInfo(responseBody.hits.total.value), + pagination: await paginationInfo(responseBody, pager), info: {}, aggregations: responseBody.aggregations, }), }; } +async function paginationInfo(responseBody, pager) { + let { format, options, ...pageInfo } = await pager.pageInfo( + responseBody.hits.total.value + ); + + return pageInfo; +} + function transformError(response) { const responseBody = { status: response.statusCode, diff --git a/src/api/response/transformer.js b/src/api/response/transformer.js new file mode 100644 index 00000000..b21fbde8 --- /dev/null +++ b/src/api/response/transformer.js @@ -0,0 +1,30 @@ +const iiifCollectionResponse = require("./iiif/collection.js"); +const opensearchResponse = require("./opensearch"); + +async function transformSearchResult(response, pager) { + if (response.statusCode === 200) { + const responseBody = JSON.parse(response.body); + const pageInfo = await pager.pageInfo(responseBody.hits.total.value); + + if (pageInfo.format === "iiif") { + return await iiifCollectionResponse.transform(response, pager); + } + + return await opensearchResponse.transform(response, pager); + } + return transformError(response); +} + +function transformError(response) { + const responseBody = { + status: response.statusCode, + error: "TODO", + }; + + return { + statusCode: response.statusCode, + body: JSON.stringify(responseBody), + }; +} + +module.exports = { transformSearchResult }; diff --git a/src/handlers/search.js b/src/handlers/search.js index c61e00dc..844c01ad 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,59 +1,80 @@ const middleware = require("./middleware"); const { baseUrl } = require("../helpers"); -const { modelsToTargets, validModels } = require("../api/request/models"); +const { + extractRequestedModels, + modelsToTargets, + validModels, +} = require("../api/request/models"); const { search } = require("../api/opensearch"); -const opensearchResponse = require("../api/response/opensearch"); +const responseTransformer = require("../api/response/transformer"); const { decodeSearchToken, Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); const getSearch = async (event) => { event = middleware(event); - let token = event.queryStringParameters.searchToken; - if (token === undefined || token === "") { - return invalidRequest("searchToken parameter is required"); - } - let request; + const models = extractRequestedModels(event.pathParameters?.models); + const format = await responseFormat(event); + + console.log("format", format); + + let searchContext; + try { - request = await decodeSearchToken(token); - } catch (err) { - return invalidRequest("searchToken is invalid"); + searchContext = await constructSearchContext(event); + } catch (error) { + return invalidRequest(error.message); } - const page = Number(event.queryStringParameters.page || 1); - request.body.from = request.body.size * (page - 1); - - return await executeSearch(event, request.models, request.body); + return await executeSearch( + event, + models, + searchContext, + format, + getOptions(event.queryStringParameters, format) + ); }; const postSearch = async (event) => { event = middleware(event); - const eventBody = JSON.parse(event.body); - const requestedModels = - event.pathParameters?.models == null - ? ["works"] - : event.pathParameters.models.split(","); + const searchContext = JSON.parse(event.body); + const models = extractRequestedModels(event.pathParameters?.models); - return await executeSearch(event, requestedModels, eventBody); + return await executeSearch(event, models, searchContext); }; /** * Function to wrap search requests and transform responses */ -const executeSearch = async (event, models, body) => { - if (!validModels(models)) { +const executeSearch = async ( + event, + models, + searchContext, + format = "default", + options = {} +) => { + if (!validModels(models, format)) { return invalidRequest(`Invalid models requested: ${models}`); } - const pager = new Paginator(baseUrl(event), "search", models, body); - const filteredBody = new RequestPipeline(body).authFilter().toJson(); - let esResponse = await search(modelsToTargets(models), filteredBody); - let transformedResponse = await opensearchResponse.transform( - esResponse, - pager + const pager = new Paginator( + baseUrl(event), + "search", + models, + searchContext, + format, + options + ); + const filteredSearchContext = new RequestPipeline(searchContext) + .authFilter() + .toJson(); + const esResponse = await search( + modelsToTargets(models), + filteredSearchContext ); - return transformedResponse; + + return await responseTransformer.transformSearchResult(esResponse, pager); }; const invalidRequest = (message) => { @@ -63,4 +84,84 @@ const invalidRequest = (message) => { }; }; +const constructSearchContext = async (event) => { + const token = event.queryStringParameters?.searchToken; + + return token === undefined || token === "" + ? fromQueryString(event.queryStringParameters) + : await fromBody(event); +}; + +const fromQueryString = (queryStringParameters) => { + let searchContext = { + query: { + query_string: { + query: queryStringParameters?.query || "*", + }, + }, + }; + + if (queryStringParameters?.size) { + searchContext.size = queryStringParameters.size; + } + + if (queryStringParameters?.from) { + searchContext.from = queryStringParameters.from; + } + + if (queryStringParameters?.sort) { + //TODO + } + return searchContext; +}; + +const responseFormat = async (event) => { + if (event.queryStringParameters?.as) { + return event.queryStringParameters?.as; + } else { + if (event.queryStringParameters?.searchToken === undefined) + return "default"; + const token = event.queryStringParameters.searchToken; + + try { + request = await decodeSearchToken(token); + } catch (err) { + return invalidRequest("searchToken is invalid"); + } + return request.format; + } +}; + +const fromBody = async (event) => { + const token = event.queryStringParameters.searchToken; + + let request; + + try { + request = await decodeSearchToken(token); + } catch (err) { + throw new Error("searchToken is invalid"); + } + + const page = Number(event.queryStringParameters.page || 1); + request.body.from = request.body.size * (page - 1); + + return request.body; +}; + +const getOptions = (queryStringParameters, format) => { + if (format === "iiif") { + const collectionLabel = + queryStringParameters?.collectionLabel || "IIIF Collection"; + const collectionSummary = queryStringParameters?.collectionSummary || ""; + const queryString = queryStringParameters.query || "*"; + return { + collectionLabel: collectionLabel, + collectionSummary: collectionSummary, + queryString: queryString, + }; + } + return {}; +}; + module.exports = { postSearch, getSearch }; diff --git a/template.yaml b/template.yaml index 42b681b7..690b23cf 100644 --- a/template.yaml +++ b/template.yaml @@ -23,6 +23,9 @@ Parameters: CustomDomainHost: Type: String Description: Hostname within ApiDomainName for Custom Domain + DcUrl: + Type: String + Description: URL of Digital Collections website ElasticsearchEndpoint: Type: String Description: Elasticsearch url @@ -59,6 +62,7 @@ Resources: Resource: "*" Environment: Variables: + DC_URL: !Ref DcUrl ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: @@ -88,6 +92,7 @@ Resources: Resource: "*" Environment: Variables: + DC_URL: !Ref DcUrl ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: @@ -117,6 +122,7 @@ Resources: Resource: "*" Environment: Variables: + DC_URL: !Ref DcUrl ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: @@ -146,6 +152,7 @@ Resources: Resource: "*" Environment: Variables: + DC_URL: !Ref DcUrl ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: @@ -240,6 +247,7 @@ Resources: Resource: "*" Environment: Variables: + DC_URL: !Ref DcUrl ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: diff --git a/test/integration/search.test.js b/test/integration/search.test.js index 222f24a6..710ef827 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -75,19 +75,30 @@ describe("Search routes", () => { const searchToken = "N4IgRg9gJgniBcoCOBXApgJzokBbAhgC4DGAFgPr4A2VCwAvvQDQgDOAlgF5oICMADMzzQ0VVggDaIAO4QMAa3EBdekA"; - it("requires a searchToken", async () => { + it("Does not require a searchToken", async () => { + const originalQuery = { + query: { query_string: { query: "*" } }, + }; + const authQuery = new RequestPipeline(originalQuery) + .authFilter() + .toJson(); + + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); const event = helpers .mockEvent("GET", "/search") .pathPrefix("/api/v2") + .queryParams() .render(); - const result = await handler(event); - expect(result.statusCode).to.eq(400); + expect(result.statusCode).to.eq(200); const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("searchToken parameter is required"); + expect(resultBody.pagination.next_url).not.null; + expect(resultBody.pagination.current_page).to.eq(1); }); - it("requires a valid searchToken", async () => { + it("Errors on invalid searchToken", async () => { const event = helpers .mockEvent("GET", "/search") .pathPrefix("/api/v2") @@ -126,5 +137,28 @@ describe("Search routes", () => { const result = await handler(event); expect(result.statusCode).to.eq(200); }); + + it("will return a IIIF collection", async () => { + const originalQuery = { + query: { query_string: { query: "*" } }, + }; + const authQuery = new RequestPipeline(originalQuery) + .authFilter() + .toJson(); + + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + + const event = helpers + .mockEvent("GET", "/search") + .pathPrefix("/api/v2") + .queryParams({ as: "iiif" }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + const resultBody = JSON.parse(result.body); + expect(resultBody.type).to.eq("Collection"); + }); }); }); diff --git a/test/unit/api/response/iiif/collection.test.js b/test/unit/api/response/iiif/collection.test.js new file mode 100644 index 00000000..156ddd47 --- /dev/null +++ b/test/unit/api/response/iiif/collection.test.js @@ -0,0 +1,51 @@ +"use strict"; + +const transformer = require("../../../../../src/api/response/iiif/collection"); +const { Paginator } = require("../../../../../src/api/pagination"); +const chai = require("chai"); +const expect = chai.expect; + +describe("IIIF Collection response transformer", () => { + let pager; + beforeEach(() => { + pager = new Paginator( + "http://dcapi.library.northwestern.edu/v2/", + "search", + ["works"], + { query: { query_string: { query: "genre.label:architecture" } } }, + "iiif", + { + collectionLabel: "The collection label", + collectionSummary: "The collection Summary", + queryString: "genre.label:architecture", + } + ); + }); + + it("transforms a search response", async () => { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/search.json"), + }; + const result = await transformer.transform(response, pager); + expect(result.statusCode).to.eq(200); + + const body = JSON.parse(result.body); + expect(body.type).to.eq("Collection"); + expect(body.label.none[0]).to.eq("The collection label"); + }); + + it("transforms an error response", async () => { + const response = { + statusCode: 404, + body: helpers.testFixture("mocks/missing-index.json"), + }; + + const result = await transformer.transform(response, pager); + expect(result.statusCode).to.eq(404); + + const body = JSON.parse(result.body); + expect(body.status).to.eq(404); + expect(body.error).to.be.a("string"); + }); +}); From 7d16c1f09298407eab7f80a9b64614b721eaad9e Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 7 Sep 2022 04:15:03 +0000 Subject: [PATCH 19/61] Add root redirect to /docs/v2 Prevent redundant code deploys --- docs/docs/spec/openapi.yaml | 17 +- docs/docs/spec/types.yaml | 25 +- package-lock.json | 49 +- package.json | 17 +- redirect/index.js | 12 + src/package-lock.json | 2379 +++++++++++++++++++++++++++++++++++ src/package.json | 19 + template.yaml | 61 +- test/unit/redirect.test.js | 18 + 9 files changed, 2548 insertions(+), 49 deletions(-) create mode 100644 redirect/index.js create mode 100644 src/package-lock.json create mode 100644 src/package.json create mode 100644 test/unit/redirect.test.js diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index a87da77a..0b72161e 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -104,9 +104,15 @@ paths: tags: - Search parameters: + - name: query + in: query + required: false + description: Keyword query + schema: + type: string - name: searchToken in: query - required: true + required: false description: Search token from previous search response schema: type: string @@ -117,6 +123,15 @@ paths: schema: type: integer minimum: 1 + - name: as + in: query + required: false + description: Desired output format + schema: + type: string + enum: + - iiif + - opensearch responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" diff --git a/docs/docs/spec/types.yaml b/docs/docs/spec/types.yaml index eaffd83a..a14865fc 100644 --- a/docs/docs/spec/types.yaml +++ b/docs/docs/spec/types.yaml @@ -26,19 +26,26 @@ components: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/IndexDocument" - description: An array of response documents - pagination: - $ref: "#/components/schemas/PaginationInfo" + oneOf: + - $ref: "#/components/schemas/OpenSearchResponse" + - $ref: "#/components/schemas/IiifPresentationManifest" schemas: + IiifPresentationManifest: + type: object + description: A [IIIF Presentation v3.x](https://iiif.io/api/presentation/3.0/) Manifest IndexDocument: type: object description: A single index document + OpenSearchResponse: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/IndexDocument" + description: An array of response documents + pagination: + $ref: "#/components/schemas/PaginationInfo" PaginationInfo: type: object description: Pagination info for the current response diff --git a/package-lock.json b/package-lock.json index cdef0ecb..287f525e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,16 @@ { - "name": "dc-api", + "name": "dc-api-build", "version": "2.0.0pre", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "dc-api", + "name": "dc-api-build", "version": "2.0.0pre", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "^2.0.1", - "@aws-sdk/credential-provider-node": "^3.142.0", - "@aws-sdk/node-http-handler": "^3.127.0", - "@aws-sdk/protocol-http": "^3.127.0", - "@aws-sdk/signature-v4": "^3.130.0", - "axios": ">=0.21.1", - "base64-js": "^1.5.1", - "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "dc-api": "file:./src" }, "devDependencies": { "chai": "^4.2.0", @@ -1612,6 +1605,10 @@ "node": ">= 8" } }, + "node_modules/dc-api": { + "resolved": "src", + "link": true + }, "node_modules/debug": { "version": "4.3.3", "dev": true, @@ -3325,6 +3322,22 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "src": { + "name": "dc-api", + "version": "2.0.0pre", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", + "axios": ">=0.21.1", + "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", + "lz-string": "^1.4.4" + } } }, "dependencies": { @@ -4409,6 +4422,20 @@ "which": "^2.0.1" } }, + "dc-api": { + "version": "file:src", + "requires": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", + "axios": ">=0.21.1", + "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", + "lz-string": "^1.4.4" + } + }, "debug": { "version": "4.3.3", "dev": true, diff --git a/package.json b/package.json index 5dcc3dde..7cde190c 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,15 @@ { - "name": "dc-api", + "name": "dc-api-build", "version": "2.0.0pre", - "description": "NUL Digital Collections API", - "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "description": "NUL Digital Collections API Build Environment", + "repository": "https://github.com/nulib/dc-api-v2", "author": "nulib", "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "^2.0.1", - "@aws-sdk/credential-provider-node": "^3.142.0", - "@aws-sdk/node-http-handler": "^3.127.0", - "@aws-sdk/protocol-http": "^3.127.0", - "@aws-sdk/signature-v4": "^3.130.0", - "axios": ">=0.21.1", - "base64-js": "^1.5.1", - "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "dc-api": "file:./src" }, "scripts": { + "preinstall": "cd src && npm i && cd -", "prettier": "prettier -c src test", "prettier:fix": "prettier -cw src test", "test": "mocha", diff --git a/redirect/index.js b/redirect/index.js new file mode 100644 index 00000000..ca667b76 --- /dev/null +++ b/redirect/index.js @@ -0,0 +1,12 @@ +exports.handler = async () => { + const target = process.env.REDIRECT_TO; + + return { + statusCode: 302, + headers: { + "content-type": "text/html", + location: target + }, + body: `

Redirecting to API Documentation

` + } +} diff --git a/src/package-lock.json b/src/package-lock.json new file mode 100644 index 00000000..38227692 --- /dev/null +++ b/src/package-lock.json @@ -0,0 +1,2379 @@ +{ + "name": "dc-api", + "version": "2.0.0pre", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dc-api", + "version": "2.0.0pre", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", + "axios": ">=0.21.1", + "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", + "lz-string": "^1.4.4" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", + "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", + "dependencies": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", + "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", + "dependencies": { + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", + "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", + "dependencies": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.162.0.tgz", + "integrity": "sha512-8j1f/g+pNny3HkOojl+6phwd1yQE0FmM6EdssRJPA/IpR+SE0qTva2psKfZA9DivAg+/iTBozVCQU5GUJY1F2A==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/abort-controller/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.165.0.tgz", + "integrity": "sha512-Cizf03z6UFgHWOIQYOjZdNwUhoh6yhT5B2dH+mh1q+Naq1zsLmD2PUvx7SM+0fZsN9MhOICLyBSE/nSSE7E8Kg==", + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.163.0", + "@aws-sdk/fetch-http-handler": "3.162.0", + "@aws-sdk/hash-node": "3.162.0", + "@aws-sdk/invalid-dependency": "3.162.0", + "@aws-sdk/middleware-content-length": "3.162.0", + "@aws-sdk/middleware-host-header": "3.162.0", + "@aws-sdk/middleware-logger": "3.162.0", + "@aws-sdk/middleware-recursion-detection": "3.162.0", + "@aws-sdk/middleware-retry": "3.162.0", + "@aws-sdk/middleware-serde": "3.162.0", + "@aws-sdk/middleware-stack": "3.162.0", + "@aws-sdk/middleware-user-agent": "3.162.0", + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/node-http-handler": "3.162.0", + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/smithy-client": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/url-parser": "3.162.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.162.0", + "@aws-sdk/util-defaults-mode-node": "3.163.0", + "@aws-sdk/util-user-agent-browser": "3.162.0", + "@aws-sdk/util-user-agent-node": "3.162.0", + "@aws-sdk/util-utf8-browser": "3.109.0", + "@aws-sdk/util-utf8-node": "3.109.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "dependencies": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "dependencies": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/client-sso/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.163.0.tgz", + "integrity": "sha512-iBl5Zc3+VRGJy6n+aMcg++7tzYi4G1bHia6v/eF93SvdKxtRv40M9QnqoNfaNUuw9U2ltwKOHepw7J3bkOA8cQ==", + "dependencies": { + "@aws-sdk/signature-v4": "3.163.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-config-provider": "3.109.0", + "@aws-sdk/util-middleware": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/config-resolver/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.162.0.tgz", + "integrity": "sha512-yzCJXiAAbZZHB4iThi4I+rs+gTYwBSetdU4Z1D89a2xjcOjCa8IhdQKm3GO/uJMScy4VtW3EEFG4/zZ7dVQPOw==", + "dependencies": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.162.0.tgz", + "integrity": "sha512-ohrHMIu2MNauPjbE8mEMKtlEQH/VZdpNswPigaEejUGVumz0NSft9PlIn2X79sNX5Y+uXopynMQF4MZj773hTw==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/url-parser": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.165.0.tgz", + "integrity": "sha512-NrLe29bdhmpNPsEsYxUxb0hTxNMXCmz5pH2l/T9COT6SMxom1wpbB/aKwf9897Z1xvhoFi6flDQjmu//599BZw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.162.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/credential-provider-sso": "3.165.0", + "@aws-sdk/credential-provider-web-identity": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.165.0.tgz", + "integrity": "sha512-emUd3kqAvV8Qydn5pJ+YKo47UJ+B5RXNyQXMasNQsw1jxrB60j8QAIL9JGM019SzZBnHZuW3DrHClA17OVC+xQ==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.162.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/credential-provider-ini": "3.165.0", + "@aws-sdk/credential-provider-process": "3.162.0", + "@aws-sdk/credential-provider-sso": "3.165.0", + "@aws-sdk/credential-provider-web-identity": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.162.0.tgz", + "integrity": "sha512-KtmYjlCMAa0XF3IJo4dxSF+OWmRoHbrdEHGEZw+j6iCZ3Nz6Y6xCsdxun5rAKdom1QRNMDR4wX0hRAdPYobW2w==", + "dependencies": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.165.0.tgz", + "integrity": "sha512-b2BuYyUSmnfChhz5ZbnqOaLSAsnzYcwpEPEUbQUdNGPSE3QcMd0SPl3woH82095WYlXTFjwgxlOPn5ad5hdBpA==", + "dependencies": { + "@aws-sdk/client-sso": "3.165.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.162.0.tgz", + "integrity": "sha512-vy86OS5/h+Vfk1bxvWjbayyUtFNdwU+mfALin3zxJbFqneSxRBydNBomt/guJjapZE+h865lkteyOsqsYMskzQ==", + "dependencies": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.162.0.tgz", + "integrity": "sha512-DZLxxEqSMXqKteYohO4w6uoORabpETWso6wBdIFMul1BbEseqLjub1594D5RA18cqkcM2dV4ttw+boPPzOjSAw==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/querystring-builder": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/fetch-http-handler/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.162.0.tgz", + "integrity": "sha512-lfyyAb0Cd084QnUNLTkYowD8RW3L5Tb9lNnIMH6HY7uSE/obw1j/OnLUPqpey628WJ5DPyyvNBah3Vu+JVZ5Mw==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/hash-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.162.0.tgz", + "integrity": "sha512-ENZ7Jf2EcxMMdAX9/sRrt/1rzeA2WwqAKrjIacKGT9KEGQNU+omWF/h+8stLCu0Uxcg0XswFXgQCXcI1IQuZjg==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/invalid-dependency/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", + "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.162.0.tgz", + "integrity": "sha512-gwuxHPBNNkr9Ah9gTNHqJ3uIp3zeY+VC2H810+RqkG5QrxU1bGAN/zezIIbcAlXjMM9vTSfO0rxGI04nhTx0BQ==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.162.0.tgz", + "integrity": "sha512-gw5xe22P62N9yZPvrVXewM2vp70w9mLRWC1vh6pRDs0hEudAlsbXoWjB/z6jpG6ucA4Y1IOuXy5yGr9lND+zhg==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.162.0.tgz", + "integrity": "sha512-3YysLwpTZdfZkve2ytKFIwEc/WqDkxoI5kUXQq2hjsHAjLW7pEhUV00o+LJbgKjNxh38eSmmKeFlr5jnIjXHiQ==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.162.0.tgz", + "integrity": "sha512-AqoTnSX0JgoFuKPyWy0S+WUJqgfkVz6Os50azi32snjHmluEgLOmfeF0ixfxGFUVGxZp8WDuu/JVhwgTRKVuUA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.162.0.tgz", + "integrity": "sha512-9ZuTim8tnTgP7wNgj+RIdYzGhNgou6QBBX85qMIvngksRUgsd1CGR17HQTyYDZTKlZs7GvLt/L5FaJcOlpPUxA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/service-error-classification": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-middleware": "3.162.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-retry/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.162.0.tgz", + "integrity": "sha512-Vdgxbl7/o99CjeljQx3mTpY4cX7rc8YQykD49L2S61D6+Gkk9Zc4DMvaJDcxvR7ZUzRwjMTcMHlxbopcp1+UBA==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.162.0.tgz", + "integrity": "sha512-e/by4QvDl9qMQHdBnLz6n8PRglswPb3eS23qT2Wt32KVLUehMUGAf1cdns6YmYSHATK/ivFmT2QHHEnNIc+n5w==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.162.0.tgz", + "integrity": "sha512-aSCQk+oQbMPVHdncuend4jmd4MguLWjvi67OwKqdZjIKsSQfObCO8vwlfDM+ED3HcOfA0LwSxsFeSfQxC+WHxA==", + "dependencies": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.162.0.tgz", + "integrity": "sha512-PgaekXCCyz/gKkbukt9zYLBJDEVgmCm0l78q5J84yJbu0FhcZY4LaAgCHdzhsgEYWTX497hokzNc3rgLdVu46A==", + "dependencies": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.162.0.tgz", + "integrity": "sha512-9jNk9SU3nNLZ1OW+fd6zHGdByUDm0FEO3Hy+J62DvbFe16x09TnVnPAoHfZ69kjz5ZNS7Gg0wmdKjUHi9T3lJQ==", + "dependencies": { + "@aws-sdk/abort-controller": "3.162.0", + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/querystring-builder": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.162.0.tgz", + "integrity": "sha512-kQLpibZRIrF58axcKY4Pr17YGoVBKBOWKol8jI8vlDhbFJqn14pVLohv4wZ8TzG2kKhWCF+t25YQCefWz2/lkg==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/property-provider/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.162.0.tgz", + "integrity": "sha512-xMFFxwcO+x5QoQX/LRGb3BpLCIBWC9cBOULm34rYGBySd/zQqebVJOhiKTPzaRL02WZTDNxsEEQHg97Lpe8CNw==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.162.0.tgz", + "integrity": "sha512-3urwxCEpnQwa1B6fKmcr8R2Qmzr8VDttRSay5CgD/stbZ4XUzNsA6G1V36+EL1Vq4vMr1aZhriARioLDlhcz+g==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.162.0.tgz", + "integrity": "sha512-0ccaGsR1O7e3BsprdYBMwGf8gmycTv1Dfz2EB5R6MiTqzcuQJ/lxpIcRh3jhUJaD1TPlUziyrBEAxtLka3HDDQ==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.162.0.tgz", + "integrity": "sha512-AD9XL3CHFzwVWNEzdTo9aRnJl1ImqrRLlJ5zR/5ihTIJ68ZTYEiYP4vNKSCV6UfQ+vaaRNgLwiAx7JXzY54awg==", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.162.0.tgz", + "integrity": "sha512-AGxISXns+1o6Pw+SPizFJDTw4Lwm+JSwhycCNhFU3JfdLsKfLY08JV4JHlcc+TyY4a8HhnGvE3r5t2f2dPLIsA==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.163.0.tgz", + "integrity": "sha512-1iein+7iAHKcRIXaZhl/lG6JrOR/Qmk27zMqfARzxDF7o/W5arSs3DHIKytO1sOEn9zV6Mqm21dRAumD21VCCg==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.55.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-hex-encoding": "3.109.0", + "@aws-sdk/util-middleware": "3.162.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.162.0.tgz", + "integrity": "sha512-o7CwdhPvzYMvHY5dTzL2kqN8Zsl2D8pZ1mG2dPdQW9hYnutLOFK1HVv5dIzoSkp3jUwVGh6AXd1i4ZSb2d0LrA==", + "dependencies": { + "@aws-sdk/middleware-stack": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/types": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.162.0.tgz", + "integrity": "sha512-NBmuwVujH8fURDMvBHkHrYu/JAfG6Js/Bu0mC4o2Kdo5mRa3fD/N9kK0dEAxU1Rxp4wY2E++V9j2ZCw1KBGrSg==", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/url-parser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.162.0.tgz", + "integrity": "sha512-aJQ2awXYDceLAzPMQETpvI1XQd8oYuqH1EriFzXHqoJTNmYnHb7awtKSqwaS8pq48x1rS/eVtJAi85BG93fXyw==", + "dependencies": { + "@aws-sdk/querystring-parser": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/url-parser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-base64-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", + "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-base64-browser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-base64-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", + "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-base64-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.154.0.tgz", + "integrity": "sha512-TUuy7paVkBRQrB/XFCsL8iTW6g/ma0S3N8dYOiIMJdeTqTFryeyOGkBpYBgYFQL6zRMZpyu0jOM7GYEffGFOXw==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-body-length-browser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", + "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", + "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", + "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.162.0.tgz", + "integrity": "sha512-BjhuriainNy0ezFqCK//380Wc4xsJJOnq1tmPlHScQxp3g8ucfClvjOUi96XQaTrEf8c8EsYp77+JNsvrHytmw==", + "dependencies": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.163.0.tgz", + "integrity": "sha512-IM5BVeUXzLpb9m8qBSpUc2iO+topP1F7Ojq1GNrl5G/b22f7b1FCL5qkTem/UIXkkgI+efI7jr05xPRGiU73Hg==", + "dependencies": { + "@aws-sdk/config-resolver": "3.163.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", + "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", + "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.162.0.tgz", + "integrity": "sha512-jDqZZ5nst+NtzvAPIQBdQqGY14Z3HeGANGm5NUoxWp8IlHnEV7GhTFDjFgubf8mgTBCzHnvbuBY1bfkzAeXWBA==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-middleware/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", + "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.162.0.tgz", + "integrity": "sha512-FNmC2ywy1u3tbUSVCSkCwLvcbjIvj5EzAtF6I2wrMTI5PfaxVIQapKn2EecoVQgf4lsZqvGjyTxbl7SYvf9fxw==", + "dependencies": { + "@aws-sdk/types": "3.162.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.162.0.tgz", + "integrity": "sha512-OIbZlccBFwITDQJoymU0V+yqqyPEbJUExJzeiP9bxJ58h7Jxj/da24cxCMaVDYvjhP/PoflOmC5Xblonaeg+oQ==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", + "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-browser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@aws-sdk/util-utf8-node": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", + "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-node/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + }, + "dependencies": { + "@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-browser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.1.tgz", + "integrity": "sha512-P4Af7E2iJqZN2HYMdWINEg++OC2CtP1ygTx6WQCzZISQA+i3Q+K3E8S8t/PSYqUnvtfh5XVjbFT9uA+4IT8C2w==", + "requires": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.1", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz", + "integrity": "sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ==", + "requires": { + "@aws-crypto/util": "^2.0.1", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/util": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.1.tgz", + "integrity": "sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ==", + "requires": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-sdk/abort-controller": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.162.0.tgz", + "integrity": "sha512-8j1f/g+pNny3HkOojl+6phwd1yQE0FmM6EdssRJPA/IpR+SE0qTva2psKfZA9DivAg+/iTBozVCQU5GUJY1F2A==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/client-sso": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.165.0.tgz", + "integrity": "sha512-Cizf03z6UFgHWOIQYOjZdNwUhoh6yhT5B2dH+mh1q+Naq1zsLmD2PUvx7SM+0fZsN9MhOICLyBSE/nSSE7E8Kg==", + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.163.0", + "@aws-sdk/fetch-http-handler": "3.162.0", + "@aws-sdk/hash-node": "3.162.0", + "@aws-sdk/invalid-dependency": "3.162.0", + "@aws-sdk/middleware-content-length": "3.162.0", + "@aws-sdk/middleware-host-header": "3.162.0", + "@aws-sdk/middleware-logger": "3.162.0", + "@aws-sdk/middleware-recursion-detection": "3.162.0", + "@aws-sdk/middleware-retry": "3.162.0", + "@aws-sdk/middleware-serde": "3.162.0", + "@aws-sdk/middleware-stack": "3.162.0", + "@aws-sdk/middleware-user-agent": "3.162.0", + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/node-http-handler": "3.162.0", + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/smithy-client": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/url-parser": "3.162.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "@aws-sdk/util-base64-node": "3.55.0", + "@aws-sdk/util-body-length-browser": "3.154.0", + "@aws-sdk/util-body-length-node": "3.55.0", + "@aws-sdk/util-defaults-mode-browser": "3.162.0", + "@aws-sdk/util-defaults-mode-node": "3.163.0", + "@aws-sdk/util-user-agent-browser": "3.162.0", + "@aws-sdk/util-user-agent-node": "3.162.0", + "@aws-sdk/util-utf8-browser": "3.109.0", + "@aws-sdk/util-utf8-node": "3.109.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "requires": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "requires": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/config-resolver": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.163.0.tgz", + "integrity": "sha512-iBl5Zc3+VRGJy6n+aMcg++7tzYi4G1bHia6v/eF93SvdKxtRv40M9QnqoNfaNUuw9U2ltwKOHepw7J3bkOA8cQ==", + "requires": { + "@aws-sdk/signature-v4": "3.163.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-config-provider": "3.109.0", + "@aws-sdk/util-middleware": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.162.0.tgz", + "integrity": "sha512-yzCJXiAAbZZHB4iThi4I+rs+gTYwBSetdU4Z1D89a2xjcOjCa8IhdQKm3GO/uJMScy4VtW3EEFG4/zZ7dVQPOw==", + "requires": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.162.0.tgz", + "integrity": "sha512-ohrHMIu2MNauPjbE8mEMKtlEQH/VZdpNswPigaEejUGVumz0NSft9PlIn2X79sNX5Y+uXopynMQF4MZj773hTw==", + "requires": { + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/url-parser": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.165.0.tgz", + "integrity": "sha512-NrLe29bdhmpNPsEsYxUxb0hTxNMXCmz5pH2l/T9COT6SMxom1wpbB/aKwf9897Z1xvhoFi6flDQjmu//599BZw==", + "requires": { + "@aws-sdk/credential-provider-env": "3.162.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/credential-provider-sso": "3.165.0", + "@aws-sdk/credential-provider-web-identity": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.165.0.tgz", + "integrity": "sha512-emUd3kqAvV8Qydn5pJ+YKo47UJ+B5RXNyQXMasNQsw1jxrB60j8QAIL9JGM019SzZBnHZuW3DrHClA17OVC+xQ==", + "requires": { + "@aws-sdk/credential-provider-env": "3.162.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/credential-provider-ini": "3.165.0", + "@aws-sdk/credential-provider-process": "3.162.0", + "@aws-sdk/credential-provider-sso": "3.165.0", + "@aws-sdk/credential-provider-web-identity": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.162.0.tgz", + "integrity": "sha512-KtmYjlCMAa0XF3IJo4dxSF+OWmRoHbrdEHGEZw+j6iCZ3Nz6Y6xCsdxun5rAKdom1QRNMDR4wX0hRAdPYobW2w==", + "requires": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.165.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.165.0.tgz", + "integrity": "sha512-b2BuYyUSmnfChhz5ZbnqOaLSAsnzYcwpEPEUbQUdNGPSE3QcMd0SPl3woH82095WYlXTFjwgxlOPn5ad5hdBpA==", + "requires": { + "@aws-sdk/client-sso": "3.165.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.162.0.tgz", + "integrity": "sha512-vy86OS5/h+Vfk1bxvWjbayyUtFNdwU+mfALin3zxJbFqneSxRBydNBomt/guJjapZE+h865lkteyOsqsYMskzQ==", + "requires": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.162.0.tgz", + "integrity": "sha512-DZLxxEqSMXqKteYohO4w6uoORabpETWso6wBdIFMul1BbEseqLjub1594D5RA18cqkcM2dV4ttw+boPPzOjSAw==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/querystring-builder": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-base64-browser": "3.109.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/hash-node": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.162.0.tgz", + "integrity": "sha512-lfyyAb0Cd084QnUNLTkYowD8RW3L5Tb9lNnIMH6HY7uSE/obw1j/OnLUPqpey628WJ5DPyyvNBah3Vu+JVZ5Mw==", + "requires": { + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.162.0.tgz", + "integrity": "sha512-ENZ7Jf2EcxMMdAX9/sRrt/1rzeA2WwqAKrjIacKGT9KEGQNU+omWF/h+8stLCu0Uxcg0XswFXgQCXcI1IQuZjg==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz", + "integrity": "sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.162.0.tgz", + "integrity": "sha512-gwuxHPBNNkr9Ah9gTNHqJ3uIp3zeY+VC2H810+RqkG5QrxU1bGAN/zezIIbcAlXjMM9vTSfO0rxGI04nhTx0BQ==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.162.0.tgz", + "integrity": "sha512-gw5xe22P62N9yZPvrVXewM2vp70w9mLRWC1vh6pRDs0hEudAlsbXoWjB/z6jpG6ucA4Y1IOuXy5yGr9lND+zhg==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.162.0.tgz", + "integrity": "sha512-3YysLwpTZdfZkve2ytKFIwEc/WqDkxoI5kUXQq2hjsHAjLW7pEhUV00o+LJbgKjNxh38eSmmKeFlr5jnIjXHiQ==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.162.0.tgz", + "integrity": "sha512-AqoTnSX0JgoFuKPyWy0S+WUJqgfkVz6Os50azi32snjHmluEgLOmfeF0ixfxGFUVGxZp8WDuu/JVhwgTRKVuUA==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.162.0.tgz", + "integrity": "sha512-9ZuTim8tnTgP7wNgj+RIdYzGhNgou6QBBX85qMIvngksRUgsd1CGR17HQTyYDZTKlZs7GvLt/L5FaJcOlpPUxA==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/service-error-classification": "3.162.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-middleware": "3.162.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.162.0.tgz", + "integrity": "sha512-Vdgxbl7/o99CjeljQx3mTpY4cX7rc8YQykD49L2S61D6+Gkk9Zc4DMvaJDcxvR7ZUzRwjMTcMHlxbopcp1+UBA==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.162.0.tgz", + "integrity": "sha512-e/by4QvDl9qMQHdBnLz6n8PRglswPb3eS23qT2Wt32KVLUehMUGAf1cdns6YmYSHATK/ivFmT2QHHEnNIc+n5w==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.162.0.tgz", + "integrity": "sha512-aSCQk+oQbMPVHdncuend4jmd4MguLWjvi67OwKqdZjIKsSQfObCO8vwlfDM+ED3HcOfA0LwSxsFeSfQxC+WHxA==", + "requires": { + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.162.0.tgz", + "integrity": "sha512-PgaekXCCyz/gKkbukt9zYLBJDEVgmCm0l78q5J84yJbu0FhcZY4LaAgCHdzhsgEYWTX497hokzNc3rgLdVu46A==", + "requires": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/shared-ini-file-loader": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.162.0.tgz", + "integrity": "sha512-9jNk9SU3nNLZ1OW+fd6zHGdByUDm0FEO3Hy+J62DvbFe16x09TnVnPAoHfZ69kjz5ZNS7Gg0wmdKjUHi9T3lJQ==", + "requires": { + "@aws-sdk/abort-controller": "3.162.0", + "@aws-sdk/protocol-http": "3.162.0", + "@aws-sdk/querystring-builder": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/property-provider": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.162.0.tgz", + "integrity": "sha512-kQLpibZRIrF58axcKY4Pr17YGoVBKBOWKol8jI8vlDhbFJqn14pVLohv4wZ8TzG2kKhWCF+t25YQCefWz2/lkg==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/protocol-http": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.162.0.tgz", + "integrity": "sha512-xMFFxwcO+x5QoQX/LRGb3BpLCIBWC9cBOULm34rYGBySd/zQqebVJOhiKTPzaRL02WZTDNxsEEQHg97Lpe8CNw==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.162.0.tgz", + "integrity": "sha512-3urwxCEpnQwa1B6fKmcr8R2Qmzr8VDttRSay5CgD/stbZ4XUzNsA6G1V36+EL1Vq4vMr1aZhriARioLDlhcz+g==", + "requires": { + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/querystring-parser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.162.0.tgz", + "integrity": "sha512-0ccaGsR1O7e3BsprdYBMwGf8gmycTv1Dfz2EB5R6MiTqzcuQJ/lxpIcRh3jhUJaD1TPlUziyrBEAxtLka3HDDQ==", + "requires": { + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/service-error-classification": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.162.0.tgz", + "integrity": "sha512-AD9XL3CHFzwVWNEzdTo9aRnJl1ImqrRLlJ5zR/5ihTIJ68ZTYEiYP4vNKSCV6UfQ+vaaRNgLwiAx7JXzY54awg==" + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.162.0.tgz", + "integrity": "sha512-AGxISXns+1o6Pw+SPizFJDTw4Lwm+JSwhycCNhFU3JfdLsKfLY08JV4JHlcc+TyY4a8HhnGvE3r5t2f2dPLIsA==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/signature-v4": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.163.0.tgz", + "integrity": "sha512-1iein+7iAHKcRIXaZhl/lG6JrOR/Qmk27zMqfARzxDF7o/W5arSs3DHIKytO1sOEn9zV6Mqm21dRAumD21VCCg==", + "requires": { + "@aws-sdk/is-array-buffer": "3.55.0", + "@aws-sdk/types": "3.162.0", + "@aws-sdk/util-hex-encoding": "3.109.0", + "@aws-sdk/util-middleware": "3.162.0", + "@aws-sdk/util-uri-escape": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/smithy-client": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.162.0.tgz", + "integrity": "sha512-o7CwdhPvzYMvHY5dTzL2kqN8Zsl2D8pZ1mG2dPdQW9hYnutLOFK1HVv5dIzoSkp3jUwVGh6AXd1i4ZSb2d0LrA==", + "requires": { + "@aws-sdk/middleware-stack": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/types": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.162.0.tgz", + "integrity": "sha512-NBmuwVujH8fURDMvBHkHrYu/JAfG6Js/Bu0mC4o2Kdo5mRa3fD/N9kK0dEAxU1Rxp4wY2E++V9j2ZCw1KBGrSg==" + }, + "@aws-sdk/url-parser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.162.0.tgz", + "integrity": "sha512-aJQ2awXYDceLAzPMQETpvI1XQd8oYuqH1EriFzXHqoJTNmYnHb7awtKSqwaS8pq48x1rS/eVtJAi85BG93fXyw==", + "requires": { + "@aws-sdk/querystring-parser": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-base64-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz", + "integrity": "sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-base64-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz", + "integrity": "sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ==", + "requires": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-body-length-browser": { + "version": "3.154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.154.0.tgz", + "integrity": "sha512-TUuy7paVkBRQrB/XFCsL8iTW6g/ma0S3N8dYOiIMJdeTqTFryeyOGkBpYBgYFQL6zRMZpyu0jOM7GYEffGFOXw==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-body-length-node": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz", + "integrity": "sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-buffer-from": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz", + "integrity": "sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA==", + "requires": { + "@aws-sdk/is-array-buffer": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-config-provider": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz", + "integrity": "sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-defaults-mode-browser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.162.0.tgz", + "integrity": "sha512-BjhuriainNy0ezFqCK//380Wc4xsJJOnq1tmPlHScQxp3g8ucfClvjOUi96XQaTrEf8c8EsYp77+JNsvrHytmw==", + "requires": { + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-defaults-mode-node": { + "version": "3.163.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.163.0.tgz", + "integrity": "sha512-IM5BVeUXzLpb9m8qBSpUc2iO+topP1F7Ojq1GNrl5G/b22f7b1FCL5qkTem/UIXkkgI+efI7jr05xPRGiU73Hg==", + "requires": { + "@aws-sdk/config-resolver": "3.163.0", + "@aws-sdk/credential-provider-imds": "3.162.0", + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/property-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-hex-encoding": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz", + "integrity": "sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz", + "integrity": "sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-middleware": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.162.0.tgz", + "integrity": "sha512-jDqZZ5nst+NtzvAPIQBdQqGY14Z3HeGANGm5NUoxWp8IlHnEV7GhTFDjFgubf8mgTBCzHnvbuBY1bfkzAeXWBA==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-uri-escape": { + "version": "3.55.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz", + "integrity": "sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.162.0.tgz", + "integrity": "sha512-FNmC2ywy1u3tbUSVCSkCwLvcbjIvj5EzAtF6I2wrMTI5PfaxVIQapKn2EecoVQgf4lsZqvGjyTxbl7SYvf9fxw==", + "requires": { + "@aws-sdk/types": "3.162.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.162.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.162.0.tgz", + "integrity": "sha512-OIbZlccBFwITDQJoymU0V+yqqyPEbJUExJzeiP9bxJ58h7Jxj/da24cxCMaVDYvjhP/PoflOmC5Xblonaeg+oQ==", + "requires": { + "@aws-sdk/node-config-provider": "3.162.0", + "@aws-sdk/types": "3.162.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz", + "integrity": "sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w==", + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@aws-sdk/util-utf8-node": { + "version": "3.109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz", + "integrity": "sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ==", + "requires": { + "@aws-sdk/util-buffer-from": "3.55.0", + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } +} diff --git a/src/package.json b/src/package.json new file mode 100644 index 00000000..ba94319d --- /dev/null +++ b/src/package.json @@ -0,0 +1,19 @@ +{ + "name": "dc-api", + "version": "2.0.0pre", + "description": "NUL Digital Collections API", + "repository": "https://github.com/nulib/dc-api-v2", + "author": "nulib", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "^2.0.1", + "@aws-sdk/credential-provider-node": "^3.142.0", + "@aws-sdk/node-http-handler": "^3.127.0", + "@aws-sdk/protocol-http": "^3.127.0", + "@aws-sdk/signature-v4": "^3.130.0", + "axios": ">=0.21.1", + "base64-js": "^1.5.1", + "jsonwebtoken": "^8.5.1", + "lz-string": "^1.4.4" + } +} diff --git a/template.yaml b/template.yaml index 690b23cf..194f00f6 100644 --- a/template.yaml +++ b/template.yaml @@ -45,7 +45,8 @@ Resources: getCollectionsFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/get-collections.handler + CodeUri: ./src + Handler: handlers/get-collections.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -75,7 +76,8 @@ Resources: getCollectionByIdFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/get-collection-by-id.handler + CodeUri: ./src + Handler: handlers/get-collection-by-id.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -105,7 +107,8 @@ Resources: getFileSetByIdFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/get-file-set-by-id.handler + CodeUri: ./src + Handler: handlers/get-file-set-by-id.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -135,7 +138,8 @@ Resources: getWorkByIdFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/get-work-by-id.handler + CodeUri: ./src + Handler: handlers/get-work-by-id.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -165,7 +169,8 @@ Resources: getWorkThumbnailFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/get-work-thumbnail.handler + CodeUri: ./src + Handler: handlers/get-work-thumbnail.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -195,7 +200,8 @@ Resources: searchPostFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/search.postSearch + CodeUri: ./src + Handler: handlers/search.postSearch Runtime: nodejs16.x Architectures: - x86_64 @@ -230,7 +236,8 @@ Resources: searchGetFunction: Type: AWS::Serverless::Function Properties: - Handler: src/handlers/search.getSearch + CodeUri: ./src + Handler: handlers/search.getSearch Runtime: nodejs16.x Architectures: - x86_64 @@ -289,18 +296,40 @@ Resources: ApiId: !Ref V1ApiId Stage: !Ref V1ApiStage - # Documentation - docsApi: + # root API + rootApi: Type: AWS::Serverless::HttpApi Properties: - StageName: v2 + StageName: latest + rootRedirect: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./redirect + Handler: index.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 1 + Description: Redirects to latest version of docs + Environment: + Variables: + REDIRECT_TO: /docs/v2/index.html + Events: + RedirectApi: + Type: HttpApi + Properties: + ApiId: !Ref rootApi + Path: / + Method: GET + + # Documentation docsMapping: Type: AWS::ApiGatewayV2::ApiMapping Properties: - ApiMappingKey: docs/v2 DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" - ApiId: !Ref docsApi - Stage: !Ref docsApiv2Stage + ApiId: !Ref rootApi + Stage: !Ref rootApilatestStage docsBucket: Type: AWS::S3::Bucket Properties: @@ -325,7 +354,7 @@ Resources: docsIntegration: Type: AWS::ApiGatewayV2::Integration Properties: - ApiId: !Ref docsApi + ApiId: !Ref rootApi IntegrationMethod: GET IntegrationType: HTTP_PROXY IntegrationUri: !Sub "http://${docsBucket}.s3-website-us-east-1.amazonaws.com/{proxy}" @@ -333,7 +362,7 @@ Resources: docsRoute: Type: AWS::ApiGatewayV2::Route Properties: - ApiId: !Ref docsApi + ApiId: !Ref rootApi AuthorizationType: NONE - RouteKey: GET /{proxy+} + RouteKey: GET /docs/v2/{proxy+} Target: !Sub "integrations/${docsIntegration}" diff --git a/test/unit/redirect.test.js b/test/unit/redirect.test.js new file mode 100644 index 00000000..2a6ab0ce --- /dev/null +++ b/test/unit/redirect.test.js @@ -0,0 +1,18 @@ +"use strict"; + +const { handler } = require("../../redirect"); + +const chai = require("chai"); +const expect = chai.expect; + +describe("redirect", function () { + helpers.saveEnvironment(); + + it("redirects unrecognized routes to the configured path", async () => { + process.env.REDIRECT_TO = "/redirect/target"; + const event = helpers.mockEvent("GET", "/").render(); + const result = await handler(event); + expect(result.statusCode).to.eq(302); + expect(result.headers.location).to.eq("/redirect/target"); + }); +}); From 09ee2b2c3d549f91d6bf406f3a469154932bf1ba Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 7 Sep 2022 15:52:19 +0000 Subject: [PATCH 20/61] Return correct content type header for JSON responses --- src/api/response/iiif/collection.js | 3 +++ src/api/response/opensearch/index.js | 6 ++++++ test/integration/get-collections.test.js | 1 + test/integration/get-doc.test.js | 3 +++ test/integration/search.test.js | 6 ++++++ 5 files changed, 19 insertions(+) diff --git a/src/api/response/iiif/collection.js b/src/api/response/iiif/collection.js index 827e93b5..59c6ab7e 100644 --- a/src/api/response/iiif/collection.js +++ b/src/api/response/iiif/collection.js @@ -7,6 +7,9 @@ async function transform(response, pager) { return { statusCode: 200, + headers: { + "content-type": "application/json", + }, body: JSON.stringify(await buildCollection(responseBody, pageInfo)), }; } diff --git a/src/api/response/opensearch/index.js b/src/api/response/opensearch/index.js index 437b082b..0320f7b1 100644 --- a/src/api/response/opensearch/index.js +++ b/src/api/response/opensearch/index.js @@ -11,6 +11,9 @@ async function transform(response, pager) { async function transformOne(responseBody) { return { statusCode: 200, + headers: { + "content-type": "application/json", + }, body: JSON.stringify({ data: responseBody._source, info: {} }), }; } @@ -18,6 +21,9 @@ async function transformOne(responseBody) { async function transformMany(responseBody, pager) { return { statusCode: 200, + headers: { + "content-type": "application/json", + }, body: JSON.stringify({ data: extractSource(responseBody.hits.hits), pagination: await paginationInfo(responseBody, pager), diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 2e83a0b8..7d6686df 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -41,6 +41,7 @@ describe("Collections route", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); }); }); }); diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js index 768d09fe..0f0d2905 100644 --- a/test/integration/get-doc.test.js +++ b/test/integration/get-doc.test.js @@ -22,6 +22,7 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("Work"); @@ -72,6 +73,7 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("Collection"); @@ -94,6 +96,7 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("FileSet"); diff --git a/test/integration/search.test.js b/test/integration/search.test.js index 710ef827..0b29cc79 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -25,6 +25,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody).to.include.keys(["data", "pagination"]); @@ -44,6 +45,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody).to.include.keys(["data", "pagination"]); @@ -93,6 +95,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody.pagination.next_url).not.null; expect(resultBody.pagination.current_page).to.eq(1); @@ -122,6 +125,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); }); it("defaults to page 1", async () => { @@ -136,6 +140,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); }); it("will return a IIIF collection", async () => { @@ -157,6 +162,7 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); const resultBody = JSON.parse(result.body); expect(resultBody.type).to.eq("Collection"); }); From 938806834c52043b4d104731715314196358e6d3 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 26 Sep 2022 15:45:38 +0000 Subject: [PATCH 21/61] Default to full aspect thumbnail --- src/handlers/get-work-thumbnail.js | 2 +- test/integration/get-thumbnail.test.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/handlers/get-work-thumbnail.js b/src/handlers/get-work-thumbnail.js index 36c4b2d0..7a171957 100644 --- a/src/handlers/get-work-thumbnail.js +++ b/src/handlers/get-work-thumbnail.js @@ -15,7 +15,7 @@ function getAxiosResponse(url, config) { function validateRequest(event) { const id = event.pathParameters.id; - const aspect = event?.queryStringParameters?.aspect || "square"; + const aspect = event?.queryStringParameters?.aspect || "full"; const sizeParam = event?.queryStringParameters?.size || 300; const size = Number(sizeParam); diff --git a/test/integration/get-thumbnail.test.js b/test/integration/get-thumbnail.test.js index d6020375..13381fcb 100644 --- a/test/integration/get-thumbnail.test.js +++ b/test/integration/get-thumbnail.test.js @@ -21,8 +21,8 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); mock - .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") - .reply(200, helpers.testFixture("mocks/thumbnail_square.jpg"), { + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { "Content-Type": "image/jpeg", }); @@ -36,7 +36,7 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); mock - .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") .reply(403, "Forbidden", { "Content-Type": "text/plain" }); const result = await handler(event.render()); @@ -67,8 +67,8 @@ describe("Work thumbnail", () => { .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/work-1234.json")); mock - .get("/iiif/2/mbk-dev/5678/square/!200,200/0/default.jpg") - .reply(200, helpers.testFixture("mocks/thumbnail_square.jpg"), { + .get("/iiif/2/mbk-dev/5678/full/!200,200/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { "Content-Type": "image/jpeg", }); From 907d96936dbd32bb94bf4629e6e910460690f239 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 5 Oct 2022 16:46:48 +0000 Subject: [PATCH 22/61] Implement /collections/{id}/thumbnail route --- docs/docs/spec/openapi.yaml | 32 ++ src/handlers/get-thumbnail.js | 113 +++++++ src/handlers/get-work-thumbnail.js | 83 ----- src/handlers/search.js | 2 - template.yaml | 12 +- .../mocks/collection-1234-no-thumbnail.json | 13 + test/fixtures/mocks/collection-1234.json | 5 +- .../mocks/missing-collection-1234.json | 6 + test/integration/get-thumbnail.test.js | 314 ++++++++++++------ 9 files changed, 384 insertions(+), 196 deletions(-) create mode 100644 src/handlers/get-thumbnail.js delete mode 100644 src/handlers/get-work-thumbnail.js create mode 100644 test/fixtures/mocks/collection-1234-no-thumbnail.json create mode 100644 test/fixtures/mocks/missing-collection-1234.json diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index 0b72161e..51a5d591 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -46,6 +46,38 @@ paths: responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" + /collections/{id}/thumbnail: + get: + operationId: getCollectionThumbnail + tags: + - Collection + parameters: + - $ref: "./types.yaml#/components/parameters/id" + - name: aspect + in: query + required: false + description: Desired aspect ratio + schema: + type: string + enum: + - full + - square + - name: size + in: query + required: false + description: Size of largest dimension + schema: + type: integer + minimum: 1 + maximum: 300 + responses: + 200: + description: A thumbnail image for the given collection + content: + image/jpeg: + schema: + type: string + format: binary /file-sets/{id}: get: operationId: getFileSetById diff --git a/src/handlers/get-thumbnail.js b/src/handlers/get-thumbnail.js new file mode 100644 index 00000000..4851c384 --- /dev/null +++ b/src/handlers/get-thumbnail.js @@ -0,0 +1,113 @@ +const { apiToken } = require("../aws/environment"); +const axios = require("axios").default; +const middleware = require("./middleware"); +const { getCollection, getWork } = require("../api/opensearch"); +const opensearchResponse = require("../api/response/opensearch"); + +function getAxiosResponse(url, config) { + return new Promise((resolve) => { + axios + .get(url, config) + .then((response) => resolve(response)) + .catch((error) => resolve(error.response)); + }); +} + +function validateRequest(event) { + const id = event.pathParameters.id; + const aspect = event?.queryStringParameters?.aspect || "full"; + const sizeParam = event?.queryStringParameters?.size || 300; + const size = Number(sizeParam); + + if (!["full", "square"].includes(aspect)) + throw new Error(`Unknown aspect ratio: ${aspect}`); + if (isNaN(size)) throw new Error(`${sizeParam} is not a valid size`); + if (size > 300) + throw new Error(`Requested size of ${size}px exceeds maximum of 300px`); + + return { id, aspect, size }; +} + +const getWorkThumbnail = async (id, aspect, size) => { + const esResponse = await getWork(id); + if (esResponse.statusCode != 200) { + return opensearchResponse.transform(esResponse); + } + + const body = JSON.parse(esResponse.body); + const iiif_base = body?._source?.representative_file_set?.url; + + if (!iiif_base) { + return { + statusCode: 404, + headers: { "content-type": "text/plain" }, + body: "Not Found", + }; + } + + const thumbnail = `${iiif_base}/${aspect}/!${size},${size}/0/default.jpg`; + + const { status, headers, data } = await getAxiosResponse(thumbnail, { + headers: { Authorization: `Bearer ${apiToken()}` }, + responseType: "arraybuffer", + }); + + if (status != 200) { + return { + statusCode: status, + body: data.toString(), + headers: headers, + }; + } + + return { + statusCode: status, + isBase64Encoded: true, + body: data.toString("base64"), + headers: { + "content-type": headers["content-type"], + }, + }; +}; + +const getParameters = async (event) => { + const { id, aspect, size } = validateRequest(middleware(event)); + if (event.rawPath.match(/\/collections\//)) { + const esResponse = await getCollection(id); + if (esResponse.statusCode != 200) { + return { error: opensearchResponse.transform(esResponse) }; + } + + const body = JSON.parse(esResponse.body); + const workId = body?._source?.representative_image?.work_id; + return { id: workId, aspect, size }; + } else { + return { id, aspect, size }; + } +}; + +/** + * A simple function to proxy a Collection or Work thumbnail from the IIIF server + */ +exports.handler = async (event) => { + try { + const { id, aspect, size, error } = await getParameters(event); + if (error) return error; + + if (!id) { + return { + statusCode: 404, + headers: { "content-type": "text/plain" }, + body: "Not Found", + }; + } + + return await getWorkThumbnail(id, aspect, size); + } catch (err) { + return { + statusCode: 400, + headers: { "content-type": "text/plain" }, + body: err.message, + }; + } +}; diff --git a/src/handlers/get-work-thumbnail.js b/src/handlers/get-work-thumbnail.js deleted file mode 100644 index 7a171957..00000000 --- a/src/handlers/get-work-thumbnail.js +++ /dev/null @@ -1,83 +0,0 @@ -const { apiToken } = require("../aws/environment"); -const axios = require("axios").default; -const middleware = require("./middleware"); -const { getWork } = require("../api/opensearch"); -const opensearchResponse = require("../api/response/opensearch"); - -function getAxiosResponse(url, config) { - return new Promise((resolve) => { - axios - .get(url, config) - .then((response) => resolve(response)) - .catch((error) => resolve(error.response)); - }); -} - -function validateRequest(event) { - const id = event.pathParameters.id; - const aspect = event?.queryStringParameters?.aspect || "full"; - const sizeParam = event?.queryStringParameters?.size || 300; - const size = Number(sizeParam); - - if (!["full", "square"].includes(aspect)) - throw new Error(`Unknown aspect ratio: ${aspect}`); - if (isNaN(size)) throw new Error(`${sizeParam} is not a valid size`); - if (size > 300) - throw new Error(`Requested size of ${size}px exceeds maximum of 300px`); - - return { id, aspect, size }; -} - -/** - * A simple function to proxy a Work's thumbnail from the IIIF server - */ -exports.handler = async (event) => { - try { - const { id, aspect, size } = validateRequest(middleware(event)); - const esResponse = await getWork(id); - if (esResponse.statusCode != 200) { - return opensearchResponse.transform(esResponse); - } - - const body = JSON.parse(esResponse.body); - const iiif_base = body?._source?.representative_file_set?.url; - - if (!iiif_base) { - return { - statusCode: 404, - headers: { "content-type": "text/plain" }, - body: "Not Found", - }; - } - - const thumbnail = `${iiif_base}/${aspect}/!${size},${size}/0/default.jpg`; - - const { status, headers, data } = await getAxiosResponse(thumbnail, { - headers: { Authorization: `Bearer ${apiToken()}` }, - responseType: "arraybuffer", - }); - - if (status != 200) { - return { - statusCode: status, - body: data.toString(), - headers: headers, - }; - } - - return { - statusCode: status, - isBase64Encoded: true, - body: data.toString("base64"), - headers: { - "content-type": headers["content-type"], - }, - }; - } catch (err) { - return { - statusCode: 400, - headers: { "content-type": "text/plain" }, - body: err.message, - }; - } -}; diff --git a/src/handlers/search.js b/src/handlers/search.js index 844c01ad..b8eaf0ff 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -16,8 +16,6 @@ const getSearch = async (event) => { const models = extractRequestedModels(event.pathParameters?.models); const format = await responseFormat(event); - console.log("format", format); - let searchContext; try { diff --git a/template.yaml b/template.yaml index 194f00f6..e61de486 100644 --- a/template.yaml +++ b/template.yaml @@ -166,11 +166,11 @@ Resources: ApiId: !Ref dcApi Path: /works/{id} Method: GET - getWorkThumbnailFunction: + getThumbnailFunction: Type: AWS::Serverless::Function Properties: CodeUri: ./src - Handler: handlers/get-work-thumbnail.handler + Handler: handlers/get-thumbnail.handler Runtime: nodejs16.x Architectures: - x86_64 @@ -191,7 +191,13 @@ Resources: ENV_PREFIX: !Ref EnvironmentPrefix ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: - Api: + CollectionApi: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /collections/{id}/thumbnail + Method: GET + WorkApi: Type: HttpApi Properties: ApiId: !Ref dcApi diff --git a/test/fixtures/mocks/collection-1234-no-thumbnail.json b/test/fixtures/mocks/collection-1234-no-thumbnail.json new file mode 100644 index 00000000..6bfe3e0e --- /dev/null +++ b/test/fixtures/mocks/collection-1234-no-thumbnail.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-collection", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Collection", + "published": true, + "representative_image": {} + } +} diff --git a/test/fixtures/mocks/collection-1234.json b/test/fixtures/mocks/collection-1234.json index 0c68fa5a..6dbe04cb 100644 --- a/test/fixtures/mocks/collection-1234.json +++ b/test/fixtures/mocks/collection-1234.json @@ -7,6 +7,9 @@ "_source": { "id": "1234", "api_model": "Collection", - "published": true + "published": true, + "representative_image": { + "work_id": "1234" + } } } diff --git a/test/fixtures/mocks/missing-collection-1234.json b/test/fixtures/mocks/missing-collection-1234.json new file mode 100644 index 00000000..6e65c59f --- /dev/null +++ b/test/fixtures/mocks/missing-collection-1234.json @@ -0,0 +1,6 @@ +{ + "_index": "dev-dc-v2-collection", + "_type": "_doc", + "_id": "1234", + "found": false +} diff --git a/test/integration/get-thumbnail.test.js b/test/integration/get-thumbnail.test.js index 13381fcb..366cc4ef 100644 --- a/test/integration/get-thumbnail.test.js +++ b/test/integration/get-thumbnail.test.js @@ -2,124 +2,224 @@ const chai = require("chai"); const expect = chai.expect; -const { handler } = require("../../src/handlers/get-work-thumbnail"); +const { handler } = require("../../src/handlers/get-thumbnail"); -describe("Work thumbnail", () => { +describe("Thumbnail routes", () => { helpers.saveEnvironment(); const mock = helpers.mockIndex(); - const event = helpers - .mockEvent("GET", "/works/{id}/thumbnail") - .pathPrefix("/api/v2") - .pathParams({ id: 1234 }); beforeEach(() => { process.env.API_TOKEN_SECRET = "abcdef"; }); - it("retrieves a thumbnail", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/work-1234.json")); - mock - .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") - .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { - "Content-Type": "image/jpeg", - }); - - const result = await handler(event.render()); - expect(result.statusCode).to.eq(200); - expect(result.headers["content-type"]).to.eq("image/jpeg"); + describe("Collection", () => { + const event = helpers + .mockEvent("GET", "/collections/{id}/thumbnail") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }); + + it("retrieves a thumbnail", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { + "Content-Type": "image/jpeg", + }); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(200); + expect(result.headers["content-type"]).to.eq("image/jpeg"); + }); + + it("returns an error from the IIIF server", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(403, "Forbidden", { "Content-Type": "text/plain" }); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(403); + expect(result.body).to.eq("Forbidden"); + }); + + it("returns 404 if the collection doc can't be found", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-collection-1234.json")); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); + + it("returns 404 if the work doc can't be found", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); + + it("returns 404 if the collection doc has no representative work", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply( + 200, + helpers.testFixture("mocks/collection-1234-no-thumbnail.json") + ); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); + + it("returns 404 if the work doc has no thumbnail", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234-no-thumbnail.json")); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); }); - it("returns an error from the IIIF server", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/work-1234.json")); - mock - .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") - .reply(403, "Forbidden", { "Content-Type": "text/plain" }); - - const result = await handler(event.render()); - expect(result.statusCode).to.eq(403); - expect(result.body).to.eq("Forbidden"); + describe("Work", () => { + const event = helpers + .mockEvent("GET", "/works/{id}/thumbnail") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }); + + it("retrieves a thumbnail", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { + "Content-Type": "image/jpeg", + }); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(200); + expect(result.headers["content-type"]).to.eq("image/jpeg"); + }); + + it("returns an error from the IIIF server", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(403, "Forbidden", { "Content-Type": "text/plain" }); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(403); + expect(result.body).to.eq("Forbidden"); + }); + + it("returns 404 if the work doc can't be found", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); + + it("returns 404 if the work doc has no thumbnail", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234-no-thumbnail.json")); + + const result = await handler(event.render()); + expect(result.statusCode).to.eq(404); + }); }); - it("returns 404 if the work doc can't be found", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); - - const result = await handler(event.render()); - expect(result.statusCode).to.eq(404); - }); - - it("returns 404 if the work doc has no thumbnail", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/work-1234-no-thumbnail.json")); - - const result = await handler(event.render()); - expect(result.statusCode).to.eq(404); - }); - - it("accepts a proper size", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .reply(200, helpers.testFixture("mocks/work-1234.json")); - mock - .get("/iiif/2/mbk-dev/5678/full/!200,200/0/default.jpg") - .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { - "Content-Type": "image/jpeg", - }); - - const result = await handler(event.queryParams({ size: 200 }).render()); - expect(result.statusCode).to.eq(200); - }); - - it("rejects invalid sizes", async () => { - let result = await handler(event.queryParams({ size: "foo" }).render()); - expect(result.statusCode).to.eq(400); - expect(result.body).to.contain("foo is not"); - - result = await handler(event.queryParams({ size: 500 }).render()); - expect(result.statusCode).to.eq(400); - expect(result.body).to.contain("500px"); - }); - - it("accepts proper aspect ratios", async () => { - mock - .get("/dc-v2-work/_doc/1234") - .times(2) - .reply(200, helpers.testFixture("mocks/work-1234.json")); - - let resultFixture = "mocks/thumbnail_full.jpg"; - mock - .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") - .reply(200, helpers.testFixture(resultFixture), { - "Content-Type": "image/jpeg", - }); - - let result = await handler(event.queryParams({ aspect: "full" }).render()); - expect(result.statusCode).to.eq(200); - let expected = helpers.encodedFixture(resultFixture); - expect(result.body).to.eq(expected); - - resultFixture = "mocks/thumbnail_square.jpg"; - mock - .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") - .reply(200, helpers.testFixture(resultFixture), { - "Content-Type": "image/jpeg", - }); - - result = await handler(event.queryParams({ aspect: "square" }).render()); - expect(result.statusCode).to.eq(200); - expected = helpers.encodedFixture(resultFixture); - expect(result.body).to.eq(expected); - }); - - it("rejects improper aspect ratio", async () => { - const result = await handler(event.queryParams({ aspect: "foo" }).render()); - expect(result.statusCode).to.eq(400); - expect(result.body).to.contain("Unknown aspect ratio: foo"); + describe("QueryString parameters", () => { + const event = helpers + .mockEvent("GET", "/works/{id}/thumbnail") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }); + + it("accepts a proper size", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + mock + .get("/iiif/2/mbk-dev/5678/full/!200,200/0/default.jpg") + .reply(200, helpers.testFixture("mocks/thumbnail_full.jpg"), { + "Content-Type": "image/jpeg", + }); + + const result = await handler(event.queryParams({ size: 200 }).render()); + expect(result.statusCode).to.eq(200); + }); + + it("rejects invalid sizes", async () => { + let result = await handler(event.queryParams({ size: "foo" }).render()); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("foo is not"); + + result = await handler(event.queryParams({ size: 500 }).render()); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("500px"); + }); + + it("accepts proper aspect ratios", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .times(2) + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + let resultFixture = "mocks/thumbnail_full.jpg"; + mock + .get("/iiif/2/mbk-dev/5678/full/!300,300/0/default.jpg") + .reply(200, helpers.testFixture(resultFixture), { + "Content-Type": "image/jpeg", + }); + + let result = await handler( + event.queryParams({ aspect: "full" }).render() + ); + expect(result.statusCode).to.eq(200); + let expected = helpers.encodedFixture(resultFixture); + expect(result.body).to.eq(expected); + + resultFixture = "mocks/thumbnail_square.jpg"; + mock + .get("/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg") + .reply(200, helpers.testFixture(resultFixture), { + "Content-Type": "image/jpeg", + }); + + result = await handler(event.queryParams({ aspect: "square" }).render()); + expect(result.statusCode).to.eq(200); + expected = helpers.encodedFixture(resultFixture); + expect(result.body).to.eq(expected); + }); + + it("rejects improper aspect ratio", async () => { + const result = await handler( + event.queryParams({ aspect: "foo" }).render() + ); + expect(result.statusCode).to.eq(400); + expect(result.body).to.contain("Unknown aspect ratio: foo"); + }); }); }); From 3942a7350060dd1c6d644df657e9e20a47c1eefa Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Fri, 7 Oct 2022 20:12:38 +0000 Subject: [PATCH 23/61] Remove searchToken from /collections route Also add support for size parameter --- src/api/pagination.js | 17 ++++++++--- src/handlers/get-collections.js | 35 ++++++++++++---------- test/integration/get-collections.test.js | 37 ++++++++++++------------ 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/api/pagination.js b/src/api/pagination.js index c348aaab..b42ade0d 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -22,6 +22,7 @@ async function encodeSearchToken(models, body, format, options) { function from(body) { return body?.from || 0; } + function size(body) { return body?.size || 10; } @@ -55,10 +56,18 @@ class Paginator { async pageInfo(count) { let url = new URL(this.route, this.baseUrl); - url.searchParams.set( - "searchToken", - await encodeSearchToken(this.models, this.body, this.format, this.options) - ); + + if (this.options?.includeToken != false) { + url.searchParams.set( + "searchToken", + await encodeSearchToken( + this.models, + this.body, + this.format, + this.options + ) + ); + } const prev = prevPage(this.body, count); const next = nextPage(this.body, count); diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index 1342bb14..8b5414aa 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -3,33 +3,36 @@ const { baseUrl } = require("../helpers"); const { modelsToTargets } = require("../api/request/models"); const { search } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); -const { decodeSearchToken, Paginator } = require("../api/pagination"); +const { Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); +const numberParam = (value, defaultValue) => { + if (value === undefined) return defaultValue; + return Number(value); +}; + /** * A simple function to get Collections */ exports.handler = async (event) => { event = middleware(event); - let token = event?.queryStringParameters?.searchToken; + const page = numberParam(event.queryStringParameters?.page, 1); + if (isNaN(page) || page < 1) return invalidRequest("page must be >= 1"); + const size = numberParam(event.queryStringParameters?.size, 10); + if (isNaN(size) || size < 1) return invalidRequest("size must be >= 1"); + let body = { size: size, from: size * (page - 1) }; let models = ["collections"]; - let body = ""; - if (token) { - try { - const request = await decodeSearchToken(token); - const page = Number(event.queryStringParameters.page || 1); - request.body.from = request.body.size * (page - 1); - models = request.models; - body = request.body; - } catch (err) { - return invalidRequest("searchToken is invalid"); - } - } - - const pager = new Paginator(baseUrl(event), "collections", models, body); + const pager = new Paginator( + baseUrl(event), + "collections", + models, + body, + null, + { includeToken: false } + ); const filteredBody = new RequestPipeline(body).authFilter().toJson(); let esResponse = await search(modelsToTargets(models), filteredBody); let transformedResponse = await opensearchResponse.transform( diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 7d6686df..92ffb5ea 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -13,32 +13,33 @@ describe("Collections route", () => { const handler = getCollectionsHandler.handler; const originalQuery = { size: 10, from: 0 }; const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); - const searchToken = - "N4IgRg9gJgniBcoDOBLAXgUwQRgAwF8AaEAW2gwBskEBtEAYwgoo3oBcUIA7agXXyA"; + const baseEvent = helpers + .mockEvent("GET", "/collections") + .pathPrefix("/api/v2"); - it("requires a valid searchToken", async () => { - const event = helpers - .mockEvent("GET", "/collections") - .pathPrefix("/api/v2") - .queryParams({ searchToken: "Ceci n'est pas une searchToken" }) - .render(); + describe("validates parameters", () => { + it("page", async () => { + const event = baseEvent.queryParams({ page: 0 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("page must be >= 1"); + }); - const result = await handler(event); - expect(result.statusCode).to.eq(400); - const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("searchToken is invalid"); + it("size", async () => { + const event = baseEvent.queryParams({ size: 0 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("size must be >= 1"); + }); }); it("paginates results using a searchToken and page number", async () => { mock .post("/dc-v2-collection/_search", authQuery) .reply(200, helpers.testFixture("mocks/collections.json")); - const event = helpers - .mockEvent("GET", "/collections") - .pathPrefix("/api/v2") - .queryParams({ searchToken, page: 1 }) - .body(authQuery) - .render(); + const event = baseEvent.queryParams({ page: 1 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(200); expect(result.headers).to.include({ "content-type": "application/json" }); From 83b080faee7e7e9ed9b9fbb4ea1ea27401c6b170 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 10 Oct 2022 15:21:35 +0000 Subject: [PATCH 24/61] Allow paginator to take additional params for the base url Include non-default size in /collections responses --- src/api/pagination.js | 7 ++++++ src/handlers/get-collections.js | 16 +++++++----- test/integration/get-collections.test.js | 32 ++++++++++++++++++++---- test/unit/api/pagination.test.js | 14 +++++++++++ 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/api/pagination.js b/src/api/pagination.js index b42ade0d..d3710742 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -69,6 +69,13 @@ class Paginator { ); } + const extraParams = this?.options?.extraParams; + if (typeof extraParams === "object") { + for (const param in extraParams) { + url.searchParams.set(param, extraParams[param]); + } + } + const prev = prevPage(this.body, count); const next = nextPage(this.body, count); diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index 8b5414aa..1f4c235b 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -6,9 +6,12 @@ const opensearchResponse = require("../api/response/opensearch"); const { Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); +const DefaultSize = 10; + const numberParam = (value, defaultValue) => { - if (value === undefined) return defaultValue; - return Number(value); + const result = Number(value); + if (isNaN(result)) return defaultValue; + return result; }; /** @@ -18,20 +21,21 @@ exports.handler = async (event) => { event = middleware(event); const page = numberParam(event.queryStringParameters?.page, 1); - if (isNaN(page) || page < 1) return invalidRequest("page must be >= 1"); - const size = numberParam(event.queryStringParameters?.size, 10); - if (isNaN(size) || size < 1) return invalidRequest("size must be >= 1"); + if (page < 1) return invalidRequest("page must be >= 1"); + const size = numberParam(event.queryStringParameters?.size, DefaultSize); + if (size < 1) return invalidRequest("size must be >= 1"); let body = { size: size, from: size * (page - 1) }; let models = ["collections"]; + const extraParams = size === DefaultSize ? {} : { size }; const pager = new Paginator( baseUrl(event), "collections", models, body, null, - { includeToken: false } + { extraParams, includeToken: false } ); const filteredBody = new RequestPipeline(body).authFilter().toJson(); let esResponse = await search(modelsToTargets(models), filteredBody); diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 92ffb5ea..4bec07bb 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -11,11 +11,11 @@ describe("Collections route", () => { describe("GET /collections", () => { const handler = getCollectionsHandler.handler; - const originalQuery = { size: 10, from: 0 }; - const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); const baseEvent = helpers .mockEvent("GET", "/collections") .pathPrefix("/api/v2"); + const makeQuery = (params) => + new RequestPipeline(params).authFilter().toJson(); describe("validates parameters", () => { it("page", async () => { @@ -35,14 +35,36 @@ describe("Collections route", () => { }); }); - it("paginates results using a searchToken and page number", async () => { + it("paginates results using default size and page number", async () => { mock - .post("/dc-v2-collection/_search", authQuery) + .post("/dc-v2-collection/_search", makeQuery({ size: 10, from: 0 })) .reply(200, helpers.testFixture("mocks/collections.json")); - const event = baseEvent.queryParams({ page: 1 }).render(); + const event = baseEvent.render(); const result = await handler(event); expect(result.statusCode).to.eq(200); expect(result.headers).to.include({ "content-type": "application/json" }); + const { + pagination: { query_url }, + } = JSON.parse(result.body); + const url = new URL(query_url); + expect(url.searchParams.has("searchToken")).to.be.false; + expect(url.searchParams.has("size")).to.be.false; + }); + + it("paginates results using provided size and page number", async () => { + mock + .post("/dc-v2-collection/_search", makeQuery({ size: 5, from: 10 })) + .reply(200, helpers.testFixture("mocks/collections.json")); + const event = baseEvent.queryParams({ page: 3, size: 5 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + const { + pagination: { query_url }, + } = JSON.parse(result.body); + const url = new URL(query_url); + expect(url.searchParams.has("searchToken")).to.be.false; + expect(url.searchParams.get("size")).to.eq("5"); }); }); }); diff --git a/test/unit/api/pagination.test.js b/test/unit/api/pagination.test.js index bf5313d9..218d5e4c 100644 --- a/test/unit/api/pagination.test.js +++ b/test/unit/api/pagination.test.js @@ -72,4 +72,18 @@ describe("Paginator", function () { const result = await pager.pageInfo(1275); expect(result.limit).to.eq(10); }); + + it("excludes searchToken when required", async () => { + pager.options = { includeToken: false }; + const result = await pager.pageInfo(1275); + const url = new URL(result.query_url); + expect(url.searchParams.has("searchToken")).to.be.false; + }); + + it("includes extra parameters", async () => { + pager.options = { extraParams: { size: 5 } }; + const result = await pager.pageInfo(1275); + const url = new URL(result.query_url); + expect(url.searchParams.get("size")).to.eq("5"); + }); }); From 7bf373475860603a5034854aec8d3bdd22226415 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Wed, 5 Oct 2022 15:59:18 +0000 Subject: [PATCH 25/61] Implement API auth route handlers, tests, and configuration Co-Authored-By: Brendan Quinn --- .github/workflows/build.yml | 20 ++--- package-lock.json | 15 ++++ src/aws/environment.js | 12 ++- src/handlers/get-auth-callback.js | 59 +++++++++++++++ src/handlers/get-auth-login.js | 50 +++++++++++++ src/handlers/get-auth-whoami.js | 28 +++++++ src/package-lock.json | 14 ++++ src/package.json | 1 + template.yaml | 87 +++++++++++++++++++++- test/integration/get-auth-callback.test.js | 34 +++++++++ test/integration/get-auth-login.test.js | 30 ++++++++ test/integration/get-auth-whoami.test.js | 42 +++++++++++ 12 files changed, 379 insertions(+), 13 deletions(-) create mode 100644 src/handlers/get-auth-callback.js create mode 100644 src/handlers/get-auth-login.js create mode 100644 src/handlers/get-auth-whoami.js create mode 100644 test/integration/get-auth-callback.test.js create mode 100644 test/integration/get-auth-login.test.js create mode 100644 test/integration/get-auth-whoami.test.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4668e08..da7e09bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,13 +8,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ci runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: 'npm' - - run: npm ci - - name: Check code style - run: npm run prettier - - name: Run tests - run: npm run test:coverage + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: "npm" + - run: npm ci + - name: Check code style + run: npm run prettier + - name: Run tests + run: npm run test:coverage diff --git a/package-lock.json b/package-lock.json index 287f525e..cbcb5974 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1592,6 +1592,14 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -3335,6 +3343,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" } @@ -4413,6 +4422,11 @@ } } }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, "cross-spawn": { "version": "7.0.3", "dev": true, @@ -4432,6 +4446,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" } diff --git a/src/aws/environment.js b/src/aws/environment.js index c89c99f6..c2fc9871 100644 --- a/src/aws/environment.js +++ b/src/aws/environment.js @@ -9,6 +9,10 @@ function apiToken() { return jwt.sign(token, process.env.API_TOKEN_SECRET); } +function dcApiEndpoint() { + return process.env.DC_API_ENDPOINT; +} + function elasticsearchEndpoint() { return process.env.ELASTICSEARCH_ENDPOINT; } @@ -23,4 +27,10 @@ function region() { return process.env.AWS_REGION || "us-east-1"; } -module.exports = { apiToken, elasticsearchEndpoint, prefix, region }; +module.exports = { + apiToken, + dcApiEndpoint, + elasticsearchEndpoint, + prefix, + region, +}; diff --git a/src/handlers/get-auth-callback.js b/src/handlers/get-auth-callback.js new file mode 100644 index 00000000..c0740cb3 --- /dev/null +++ b/src/handlers/get-auth-callback.js @@ -0,0 +1,59 @@ +const axios = require("axios").default; +const cookie = require("cookie"); +const jwt = require("jsonwebtoken"); + +/** + * NUSSO auth callback + */ +exports.handler = async (event) => { + const returnPath = cookie.parse(event.headers.Cookie, { + decode: function (token) { + return Buffer.from(token, "base64").toString("utf8"); + }, + })?.redirectUrl; + + const user = await redeemSsoToken(event); + let response; + if (user) { + const token = jwt.sign(user, process.env.API_TOKEN_SECRET); + response = { + statusCode: 302, + headers: { + location: returnPath, + "set-cookie": cookie.serialize("dcApiV2Token", token, { + domain: "library.northwestern.edu", + path: "/", + secure: true, + }), + }, + }; + } + + return response; +}; + +async function redeemSsoToken(event) { + const cookies = cookie.parse(event.headers.Cookie); + + if (cookies.nusso) { + try { + const response = await axios.get( + `${process.env.NUSSO_BASE_URL}validate-with-directory-search-response`, + { + headers: { + apikey: process.env.NUSSO_API_KEY, + webssotoken: cookies.nusso, + }, + } + ); + const user = response.data.results[0]; + return user; + } catch (err) { + console.error(err); + return null; + } + } else { + console.warn("No NUSSO token found in request"); + return null; + } +} diff --git a/src/handlers/get-auth-login.js b/src/handlers/get-auth-login.js new file mode 100644 index 00000000..9947818f --- /dev/null +++ b/src/handlers/get-auth-login.js @@ -0,0 +1,50 @@ +const { dcApiEndpoint } = require("../aws/environment"); +const axios = require("axios").default; +const cookie = require("cookie"); + +/** + * Performs NUSSO login + */ +exports.handler = async (event) => { + const callbackUrl = `${dcApiEndpoint()}/auth/callback`; + const url = `${process.env.NUSSO_BASE_URL}get-ldap-redirect-url`; + const returnPath = + event.queryStringParameters?.goto || event.headers?.Referer; + + if (!returnPath) { + return { + statusCode: 400, + }; + } + + let resp; + + await axios + .get(url, { + headers: { + apikey: process.env.NUSSO_API_KEY, + goto: callbackUrl, + }, + }) + .then((response) => { + resp = { + statusCode: 302, + headers: { + location: response.data.redirecturl, + "set-cookie": cookie.serialize("redirectUrl", returnPath, { + encode: function (token) { + return Buffer.from(token, "utf8").toString("base64"); + }, + }), + }, + }; + }) + .catch((error) => { + console.error("NUSSO request error", error); + resp = { + statusCode: 401, + }; + }); + + return resp; +}; diff --git a/src/handlers/get-auth-whoami.js b/src/handlers/get-auth-whoami.js new file mode 100644 index 00000000..66c47e52 --- /dev/null +++ b/src/handlers/get-auth-whoami.js @@ -0,0 +1,28 @@ +const cookie = require("cookie"); +const jwt = require("jsonwebtoken"); + +/** + * NUSSO whoami - validates JWT and returns user info + */ +exports.handler = async (event) => { + try { + const token = cookie.parse(event.headers.Cookie)?.dcApiV2Token; + const user = jwt.verify(token, process.env.API_TOKEN_SECRET); + + return { + statusCode: 200, + headers: { + "Access-Control-Allow-Origin": event.headers.Origin, + "Access-Control-Allow-Headers": "*", + "Access-Control-Allow-Methods": "POST, GET, OPTIONS", + "Access-Control-Allow-Credentials": "true", + }, + body: JSON.stringify(user), + }; + } catch (error) { + return { + statusCode: 401, + body: "Error verifying API token: " + error.message, + }; + } +}; diff --git a/src/package-lock.json b/src/package-lock.json index 38227692..0fec8066 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -16,6 +16,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" } @@ -1055,6 +1056,14 @@ "node": ">= 0.8" } }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2233,6 +2242,11 @@ "delayed-stream": "~1.0.0" } }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", diff --git a/src/package.json b/src/package.json index ba94319d..79d10710 100644 --- a/src/package.json +++ b/src/package.json @@ -13,6 +13,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "base64-js": "^1.5.1", + "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" } diff --git a/template.yaml b/template.yaml index e61de486..f6898dc4 100644 --- a/template.yaml +++ b/template.yaml @@ -23,6 +23,9 @@ Parameters: CustomDomainHost: Type: String Description: Hostname within ApiDomainName for Custom Domain + DcApiEndpoint: + Type: String + Description: URL for DC API DcUrl: Type: String Description: URL of Digital Collections website @@ -33,6 +36,12 @@ Parameters: Type: String Description: Index Prefix Default: "" + NussoApiKey: + Type: String + Description: API key for auth server + NussoBaseUrl: + Type: String + Description: Auth server URL V1ApiId: Type: String Description: ID of the v1 API to mount on /api/v1 @@ -42,6 +51,80 @@ Parameters: Default: latest Resources: # V2 API + getAuthCallbackFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/get-auth-callback.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: NUSSO callback function. + Environment: + Variables: + API_TOKEN_SECRET: !Ref ApiSecret + DC_API_ENDPOINT: !Ref DcApiEndpoint + DC_URL: !Ref DcUrl + NUSSO_API_KEY: !Ref NussoApiKey + NUSSO_BASE_URL: !Ref NussoBaseUrl + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /auth/callback + Method: GET + getAuthLoginFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/get-auth-login.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Performs NUSSO login. + Environment: + Variables: + DC_API_ENDPOINT: !Ref DcApiEndpoint + DC_URL: !Ref DcUrl + NUSSO_API_KEY: !Ref NussoApiKey + NUSSO_BASE_URL: !Ref NussoBaseUrl + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /auth/login + Method: GET + getAuthWhoAmIFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/get-auth-whoami.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Exchanges valid JWT token for user information. + Environment: + Variables: + API_TOKEN_SECRET: !Ref ApiTokenSecret + DC_API_ENDPOINT: !Ref DcApiEndpoint + DC_URL: !Ref DcUrl + NUSSO_API_KEY: !Ref NussoApiKey + NUSSO_BASE_URL: !Ref NussoBaseUrl + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /auth/whoami + Method: GET getCollectionsFunction: Type: AWS::Serverless::Function Properties: @@ -354,8 +437,8 @@ Resources: - Sid: PublicReadForGetBucketObjects Effect: Allow Principal: "*" - Action: 's3:GetObject' - Resource: !Sub "arn:aws:s3:::${docsBucket}/*" + Action: "s3:GetObject" + Resource: !Sub "arn:aws:s3:::${docsBucket}/*" Bucket: !Ref docsBucket docsIntegration: Type: AWS::ApiGatewayV2::Integration diff --git a/test/integration/get-auth-callback.test.js b/test/integration/get-auth-callback.test.js new file mode 100644 index 00000000..ba6449f2 --- /dev/null +++ b/test/integration/get-auth-callback.test.js @@ -0,0 +1,34 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const nock = require("nock"); +const getAuthCallbackHandler = require("../../src/handlers/get-auth-callback"); + +describe("auth callback", function () { + helpers.saveEnvironment(); + + it("redeems the NUSSO token", async () => { + process.env.NUSSO_BASE_URL = "https://nusso-base.com/"; + process.env.NUSSO_API_KEY = "abc123"; + process.env.API_TOKEN_SECRET = "abc123"; + + const _scope = nock(process.env.NUSSO_BASE_URL) + .get("/validate-with-directory-search-response") + .reply(200, { + results: [{ displayName: "Some User" }], + }); + + const event = helpers + .mockEvent("GET", "/auth/callback") + .pathPrefix("/api/v2") + .headers({ + Cookie: "nusso=bnVzc28=;redirectUrl=aHR0cHM6Ly9leGFtcGxlLmNvbQ==;", + }) + .render(); + + const result = await getAuthCallbackHandler.handler(event); + expect(result.statusCode).to.eq(302); + expect(result.headers.location).to.eq("https://example.com"); + }); +}); diff --git a/test/integration/get-auth-login.test.js b/test/integration/get-auth-login.test.js new file mode 100644 index 00000000..2a190ac4 --- /dev/null +++ b/test/integration/get-auth-login.test.js @@ -0,0 +1,30 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const nock = require("nock"); +const getAuthLoginHandler = require("../../src/handlers/get-auth-login"); + +describe("auth login", function () { + helpers.saveEnvironment(); + + it("redirects to the NUSSO url", async () => { + process.env.NUSSO_BASE_URL = "https://nusso-base.com/"; + process.env.NUSSO_API_KEY = "abc123"; + + const _scope = nock(process.env.NUSSO_BASE_URL) + .get("/get-ldap-redirect-url") + .reply(200, { + data: { redirecturl: "https://test-redirect.com" }, + }); + + const event = helpers + .mockEvent("GET", "/auth/login") + .pathPrefix("/api/v2") + .queryParams({ goto: "https://test-goto.com" }) + .render(); + + const result = await getAuthLoginHandler.handler(event); + expect(result.statusCode).to.eq(302); + }); +}); diff --git a/test/integration/get-auth-whoami.test.js b/test/integration/get-auth-whoami.test.js new file mode 100644 index 00000000..228b065c --- /dev/null +++ b/test/integration/get-auth-whoami.test.js @@ -0,0 +1,42 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const nock = require("nock"); +const getAuthWhoamiHandler = require("../../src/handlers/get-auth-whoami"); + +describe("auth whoami", function () { + helpers.saveEnvironment(); + + it("redeems a valid NUSSO token", async () => { + process.env.API_TOKEN_SECRET = "abc123"; + + const event = helpers + .mockEvent("GET", "/auth/whoami") + .pathPrefix("/api/v2") + .headers({ + Cookie: + "dcApiV2Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaXNwbGF5TmFtZSI6IlNvbWUgT25lIiwiaWF0IjoxNjY1NDE3NzYzfQ.Nwi8dJnc7w201ZtO5de5zYmU-F5gEalkmHZ5pR1VXms;", + }) + .render(); + + const result = await getAuthWhoamiHandler.handler(event); + expect(result.statusCode).to.eq(200); + expect(JSON.parse(result.body)).to.contain({ displayName: "Some One" }); + }); + + it("rejects an invalid or missing NUSSO token", async () => { + process.env.API_TOKEN_SECRET = "abc123"; + + const event = helpers + .mockEvent("GET", "/auth/whoami") + .pathPrefix("/api/v2") + .render(); + + const result = await getAuthWhoamiHandler.handler(event); + expect(result.statusCode).to.eq(401); + expect(result.body).to.eq( + "Error verifying API token: argument str must be a string" + ); + }); +}); From ad16b4c7524dab8fdf0faf1f1765dfec410e7e4e Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 10 Oct 2022 18:05:34 +0000 Subject: [PATCH 26/61] Fix ApiSecret reference in deploy template --- template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.yaml b/template.yaml index f6898dc4..fb326057 100644 --- a/template.yaml +++ b/template.yaml @@ -113,7 +113,7 @@ Resources: Description: Exchanges valid JWT token for user information. Environment: Variables: - API_TOKEN_SECRET: !Ref ApiTokenSecret + API_TOKEN_SECRET: !Ref ApiSecret DC_API_ENDPOINT: !Ref DcApiEndpoint DC_URL: !Ref DcUrl NUSSO_API_KEY: !Ref NussoApiKey From 3acb5dacd00e0b2a49a5124328e291a9328c3a06 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 10 Oct 2022 21:44:44 +0000 Subject: [PATCH 27/61] Use middleware to fix discrepancies between SAM and API Gateway Mostly CORS Co-Authored-By: Brendan Quinn Co-Authored-By: Karen Shaw --- src/handlers/get-auth-callback.js | 28 +++++++++-------- src/handlers/get-auth-login.js | 18 ++++++----- src/handlers/get-auth-whoami.js | 11 ++++--- src/handlers/get-collection-by-id.js | 9 +++--- src/handlers/get-collections.js | 6 ++-- src/handlers/get-file-set-by-id.js | 9 +++--- src/handlers/get-thumbnail.js | 4 +-- src/handlers/get-work-by-id.js | 9 +++--- src/handlers/middleware.js | 16 ++++++++-- src/handlers/options-request.js | 6 ++++ src/handlers/search.js | 12 +++++--- src/helpers.js | 39 ++++++++++++++++++++++-- src/package-lock.json | 25 --------------- src/package.json | 1 - template.yaml | 28 +++++++++++------ test/integration/get-auth-whoami.test.js | 3 +- test/integration/options-request.test.js | 23 ++++++++++++++ test/test-helpers/event-builder.js | 9 ++++-- 18 files changed, 164 insertions(+), 92 deletions(-) create mode 100644 src/handlers/options-request.js create mode 100644 test/integration/options-request.test.js diff --git a/src/handlers/get-auth-callback.js b/src/handlers/get-auth-callback.js index c0740cb3..2a05b429 100644 --- a/src/handlers/get-auth-callback.js +++ b/src/handlers/get-auth-callback.js @@ -1,16 +1,18 @@ const axios = require("axios").default; const cookie = require("cookie"); const jwt = require("jsonwebtoken"); +const { processRequest, processResponse } = require("./middleware"); /** * NUSSO auth callback */ exports.handler = async (event) => { - const returnPath = cookie.parse(event.headers.Cookie, { - decode: function (token) { - return Buffer.from(token, "base64").toString("utf8"); - }, - })?.redirectUrl; + event = processRequest(event); + + const returnPath = Buffer.from( + decodeURIComponent(event.cookieObject.redirectUrl), + "base64" + ).toString("utf8"); const user = await redeemSsoToken(event); let response; @@ -18,31 +20,31 @@ exports.handler = async (event) => { const token = jwt.sign(user, process.env.API_TOKEN_SECRET); response = { statusCode: 302, - headers: { - location: returnPath, - "set-cookie": cookie.serialize("dcApiV2Token", token, { + cookies: [ + cookie.serialize("dcApiV2Token", token, { domain: "library.northwestern.edu", path: "/", secure: true, }), + ], + headers: { + location: returnPath, }, }; } - return response; + return processResponse(event, response); }; async function redeemSsoToken(event) { - const cookies = cookie.parse(event.headers.Cookie); - - if (cookies.nusso) { + if (event.cookieObject.nusso) { try { const response = await axios.get( `${process.env.NUSSO_BASE_URL}validate-with-directory-search-response`, { headers: { apikey: process.env.NUSSO_API_KEY, - webssotoken: cookies.nusso, + webssotoken: event.cookieObject.nusso, }, } ); diff --git a/src/handlers/get-auth-login.js b/src/handlers/get-auth-login.js index 9947818f..4f334df5 100644 --- a/src/handlers/get-auth-login.js +++ b/src/handlers/get-auth-login.js @@ -1,15 +1,18 @@ const { dcApiEndpoint } = require("../aws/environment"); const axios = require("axios").default; const cookie = require("cookie"); +const { processRequest, processResponse } = require("./middleware"); /** * Performs NUSSO login */ exports.handler = async (event) => { + event = processRequest(event); + const callbackUrl = `${dcApiEndpoint()}/auth/callback`; const url = `${process.env.NUSSO_BASE_URL}get-ldap-redirect-url`; const returnPath = - event.queryStringParameters?.goto || event.headers?.Referer; + event.queryStringParameters?.goto || event.headers?.referer; if (!returnPath) { return { @@ -29,13 +32,14 @@ exports.handler = async (event) => { .then((response) => { resp = { statusCode: 302, + cookies: [ + cookie.serialize( + "redirectUrl", + Buffer.from(returnPath, "utf8").toString("base64") + ), + ], headers: { location: response.data.redirecturl, - "set-cookie": cookie.serialize("redirectUrl", returnPath, { - encode: function (token) { - return Buffer.from(token, "utf8").toString("base64"); - }, - }), }, }; }) @@ -46,5 +50,5 @@ exports.handler = async (event) => { }; }); - return resp; + return processResponse(event, resp); }; diff --git a/src/handlers/get-auth-whoami.js b/src/handlers/get-auth-whoami.js index 66c47e52..464f8bd3 100644 --- a/src/handlers/get-auth-whoami.js +++ b/src/handlers/get-auth-whoami.js @@ -1,24 +1,27 @@ -const cookie = require("cookie"); const jwt = require("jsonwebtoken"); +const { processRequest, processResponse } = require("./middleware"); /** * NUSSO whoami - validates JWT and returns user info */ exports.handler = async (event) => { + event = processRequest(event); + try { - const token = cookie.parse(event.headers.Cookie)?.dcApiV2Token; + const token = event.cookieObject.dcApiV2Token; const user = jwt.verify(token, process.env.API_TOKEN_SECRET); - return { + const response = { statusCode: 200, headers: { - "Access-Control-Allow-Origin": event.headers.Origin, + "Access-Control-Allow-Origin": event.headers.origin, "Access-Control-Allow-Headers": "*", "Access-Control-Allow-Methods": "POST, GET, OPTIONS", "Access-Control-Allow-Credentials": "true", }, body: JSON.stringify(user), }; + return processResponse(event, response); } catch (error) { return { statusCode: 401, diff --git a/src/handlers/get-collection-by-id.js b/src/handlers/get-collection-by-id.js index 230e5b46..e5e6754e 100644 --- a/src/handlers/get-collection-by-id.js +++ b/src/handlers/get-collection-by-id.js @@ -1,4 +1,4 @@ -const middleware = require("./middleware"); +const { processRequest, processResponse } = require("./middleware"); const { getCollection } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -6,8 +6,9 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a Collection by id */ exports.handler = async (event) => { - event = middleware(event); + event = processRequest(event); const id = event.pathParameters.id; - let esResponse = await getCollection(id); - return opensearchResponse.transform(esResponse); + const esResponse = await getCollection(id); + const response = opensearchResponse.transform(esResponse); + return processResponse(event, response); }; diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index 1f4c235b..c9018a5d 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -1,4 +1,4 @@ -const middleware = require("./middleware"); +const { processRequest, processResponse } = require("./middleware"); const { baseUrl } = require("../helpers"); const { modelsToTargets } = require("../api/request/models"); const { search } = require("../api/opensearch"); @@ -18,7 +18,7 @@ const numberParam = (value, defaultValue) => { * A simple function to get Collections */ exports.handler = async (event) => { - event = middleware(event); + event = processRequest(event); const page = numberParam(event.queryStringParameters?.page, 1); if (page < 1) return invalidRequest("page must be >= 1"); @@ -43,7 +43,7 @@ exports.handler = async (event) => { esResponse, pager ); - return transformedResponse; + return processResponse(event, transformedResponse); }; const invalidRequest = (message) => { diff --git a/src/handlers/get-file-set-by-id.js b/src/handlers/get-file-set-by-id.js index abcedee9..fc89cf6d 100644 --- a/src/handlers/get-file-set-by-id.js +++ b/src/handlers/get-file-set-by-id.js @@ -1,4 +1,4 @@ -const middleware = require("./middleware"); +const { processRequest, processResponse } = require("./middleware"); const { getFileSet } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -6,8 +6,9 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a FileSet by id */ exports.handler = async (event) => { - event = middleware(event); + event = processRequest(event); const id = event.pathParameters.id; - let esResponse = await getFileSet(id); - return opensearchResponse.transform(esResponse); + const esResponse = await getFileSet(id); + const response = opensearchResponse.transform(esResponse); + return processResponse(event, response); }; diff --git a/src/handlers/get-thumbnail.js b/src/handlers/get-thumbnail.js index 4851c384..400b07be 100644 --- a/src/handlers/get-thumbnail.js +++ b/src/handlers/get-thumbnail.js @@ -1,8 +1,8 @@ const { apiToken } = require("../aws/environment"); const axios = require("axios").default; -const middleware = require("./middleware"); const { getCollection, getWork } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); +const { processRequest } = require("./middleware"); function getAxiosResponse(url, config) { return new Promise((resolve) => { @@ -71,7 +71,7 @@ const getWorkThumbnail = async (id, aspect, size) => { }; const getParameters = async (event) => { - const { id, aspect, size } = validateRequest(middleware(event)); + const { id, aspect, size } = validateRequest(processRequest(event)); if (event.rawPath.match(/\/collections\//)) { const esResponse = await getCollection(id); if (esResponse.statusCode != 200) { diff --git a/src/handlers/get-work-by-id.js b/src/handlers/get-work-by-id.js index 96a16ec4..bfb3be2f 100644 --- a/src/handlers/get-work-by-id.js +++ b/src/handlers/get-work-by-id.js @@ -1,4 +1,4 @@ -const middleware = require("./middleware"); +const { processRequest, processResponse } = require("./middleware"); const { getWork } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); @@ -6,8 +6,9 @@ const opensearchResponse = require("../api/response/opensearch"); * A simple function to get a Work by id */ exports.handler = async (event) => { - event = middleware(event); + event = processRequest(event); const id = event.pathParameters.id; - let esResponse = await getWork(id); - return opensearchResponse.transform(esResponse); + const esResponse = await getWork(id); + const response = opensearchResponse.transform(esResponse); + return processResponse(event, response); }; diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js index 6ff9246c..4fbc881e 100644 --- a/src/handlers/middleware.js +++ b/src/handlers/middleware.js @@ -1,7 +1,19 @@ -const { decodeEventBody, normalizeHeaders } = require("../helpers"); +const { + addCorsHeaders, + decodeEventBody, + normalizeHeaders, + objectifyCookies, +} = require("../helpers"); -module.exports = function (event) { +const processRequest = function (event) { let result = normalizeHeaders(event); + result = objectifyCookies(result); result = decodeEventBody(result); return result; }; + +const processResponse = function (event, response) { + return addCorsHeaders(event, response); +}; + +module.exports = { processRequest, processResponse }; diff --git a/src/handlers/options-request.js b/src/handlers/options-request.js new file mode 100644 index 00000000..5794471a --- /dev/null +++ b/src/handlers/options-request.js @@ -0,0 +1,6 @@ +const { processRequest, processResponse } = require("./middleware"); + +module.exports.handler = async (event) => { + event = processRequest(event); + return processResponse(event, { statusCode: 200 }); +}; diff --git a/src/handlers/search.js b/src/handlers/search.js index b8eaf0ff..d4d71aa3 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,4 +1,4 @@ -const middleware = require("./middleware"); +const { processRequest, processResponse } = require("./middleware"); const { baseUrl } = require("../helpers"); const { extractRequestedModels, @@ -11,7 +11,7 @@ const { decodeSearchToken, Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); const getSearch = async (event) => { - event = middleware(event); + event = processRequest(event); const models = extractRequestedModels(event.pathParameters?.models); const format = await responseFormat(event); @@ -24,22 +24,24 @@ const getSearch = async (event) => { return invalidRequest(error.message); } - return await executeSearch( + const response = await executeSearch( event, models, searchContext, format, getOptions(event.queryStringParameters, format) ); + return processResponse(event, response); }; const postSearch = async (event) => { - event = middleware(event); + event = processRequest(event); const searchContext = JSON.parse(event.body); const models = extractRequestedModels(event.pathParameters?.models); - return await executeSearch(event, models, searchContext); + const response = await executeSearch(event, models, searchContext); + return processResponse(event, response); }; /** diff --git a/src/helpers.js b/src/helpers.js index 63fa308e..cbb13e9b 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,9 +1,22 @@ -const base64 = require("base64-js"); const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; +function addCorsHeaders(event, response) { + const allowOrigin = event?.headers?.origin || "*"; + const corsHeaders = { + "Access-Control-Allow-Origin": allowOrigin, + "Access-Control-Allow-Headers": "*", + "Access-Control-Allow-Methods": "POST, GET, OPTIONS", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Max-Age": "600", + }; + if (!response.headers) response.headers = {}; + Object.assign(response.headers, corsHeaders); + return response; +} + function decodeEventBody(event) { if (!event.isBase64Encoded) return event; - event.body = new Buffer.from(base64.toByteArray(event.body)).toString(); + event.body = Buffer.from(event.body, "base64").toString("utf8"); event.isBase64Encoded = false; return event; } @@ -69,4 +82,24 @@ function baseUrl(event) { return result.toString(); } -module.exports = { baseUrl, decodeEventBody, normalizeHeaders }; +function objectifyCookies(event) { + event.cookieObject = {}; + if (!event.cookies) return event; + const cookieRe = /^(?.+?)=(?.+)$/; + for (const cookie of event.cookies) { + const match = cookieRe.exec(cookie); + if (match) { + const { name, value } = match.groups; + event.cookieObject[name] = value; + } + } + return event; +} + +module.exports = { + addCorsHeaders, + baseUrl, + decodeEventBody, + normalizeHeaders, + objectifyCookies, +}; diff --git a/src/package-lock.json b/src/package-lock.json index 0fec8066..f8690391 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -15,7 +15,6 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", - "base64-js": "^1.5.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" @@ -1016,25 +1015,6 @@ "form-data": "^4.0.0" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -2219,11 +2199,6 @@ "form-data": "^4.0.0" } }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, "bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", diff --git a/src/package.json b/src/package.json index 79d10710..5eb63650 100644 --- a/src/package.json +++ b/src/package.json @@ -12,7 +12,6 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", - "base64-js": "^1.5.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" diff --git a/template.yaml b/template.yaml index fb326057..b0c2fb51 100644 --- a/template.yaml +++ b/template.yaml @@ -353,19 +353,27 @@ Resources: ApiId: !Ref dcApi Path: /search Method: GET + optionsFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/options-request.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 3 + Description: Handles all OPTIONS requests + Events: + Everything: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /{proxy+} + Method: OPTIONS dcApi: Type: AWS::Serverless::HttpApi Properties: - CorsConfiguration: - AllowOrigins: - - "*" - AllowHeaders: - - "*" - AllowMethods: - - GET - - POST - - OPTIONS - MaxAge: 600 StageName: v2 StageVariables: basePath: api/v2 diff --git a/test/integration/get-auth-whoami.test.js b/test/integration/get-auth-whoami.test.js index 228b065c..3fc44cca 100644 --- a/test/integration/get-auth-whoami.test.js +++ b/test/integration/get-auth-whoami.test.js @@ -2,7 +2,6 @@ const chai = require("chai"); const expect = chai.expect; -const nock = require("nock"); const getAuthWhoamiHandler = require("../../src/handlers/get-auth-whoami"); describe("auth whoami", function () { @@ -36,7 +35,7 @@ describe("auth whoami", function () { const result = await getAuthWhoamiHandler.handler(event); expect(result.statusCode).to.eq(401); expect(result.body).to.eq( - "Error verifying API token: argument str must be a string" + "Error verifying API token: jwt must be provided" ); }); }); diff --git a/test/integration/options-request.test.js b/test/integration/options-request.test.js new file mode 100644 index 00000000..aaca5045 --- /dev/null +++ b/test/integration/options-request.test.js @@ -0,0 +1,23 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const optionsHandler = require("../../src/handlers/options-request"); + +describe("OPTIONS handler", async () => { + const event = helpers + .mockEvent("OPTIONS", "/auth/whoami") + .pathPrefix("/api/v2") + .headers({ + Origin: "https://dc.library.northwestern.edu/origin-test-path", + }) + .render(); + + it("sends the correct CORS headers", async () => { + const response = await optionsHandler.handler(event); + expect(response.headers).to.contain({ + "Access-Control-Allow-Origin": + "https://dc.library.northwestern.edu/origin-test-path", + }); + }); +}); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index b9f1cd40..15ccf742 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -1,5 +1,3 @@ -const base64 = require("base64-js"); - module.exports = class { constructor(method, route) { const now = new Date(); @@ -94,7 +92,12 @@ module.exports = class { if (this._base64Encode) { result.isBase64Encoded = true; - result.body = base64.fromByteArray(new Buffer.from(result.body)); + result.body = Buffer.from(result.body, "utf8").toString("base64"); + } + + const cookies = result.headers.cookie || result.headers.Cookie; + if (cookies) { + result.cookies = cookies.split(/;\s*/); } result.pathParameters = { ...this._pathParams }; From 164f3fd26549ff618b0affbab938a0c6154258f4 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Wed, 12 Oct 2022 18:11:00 +0000 Subject: [PATCH 28/61] Shared links API implementation --- src/api/opensearch.js | 8 ++- src/handlers/get-shared-link-by-id.js | 29 +++++++++ template.yaml | 30 +++++++++ .../mocks/expired-shared-link-9101112.json | 13 ++++ .../mocks/missing-shared-link-5678.json | 6 ++ test/fixtures/mocks/shared-link-1234.json | 13 ++++ .../integration/get-shared-link-by-id.test.js | 64 +++++++++++++++++++ 7 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 src/handlers/get-shared-link-by-id.js create mode 100644 test/fixtures/mocks/expired-shared-link-9101112.json create mode 100644 test/fixtures/mocks/missing-shared-link-5678.json create mode 100644 test/fixtures/mocks/shared-link-1234.json create mode 100644 test/integration/get-shared-link-by-id.test.js diff --git a/src/api/opensearch.js b/src/api/opensearch.js index d22816ff..f8b52e01 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -14,12 +14,16 @@ async function getWork(id) { return getDocument("dc-v2-work", id); } +async function getSharedLink(id) { + return getDocument("shared_links", id); +} + async function getDocument(index, id) { const request = initRequest(`/${prefix(index)}/_doc/${id}`); let response = await awsFetch(request); if (response.statusCode === 200) { const body = JSON.parse(response.body); - if (!isVisible(body)) { + if (index != "shared_links" && !isVisible(body)) { let responseBody = { _index: prefix(index), _type: "_doc", @@ -78,4 +82,4 @@ async function search(targets, body) { return await awsFetch(request); } -module.exports = { getCollection, getFileSet, getWork, search }; +module.exports = { getCollection, getFileSet, getSharedLink, getWork, search }; diff --git a/src/handlers/get-shared-link-by-id.js b/src/handlers/get-shared-link-by-id.js new file mode 100644 index 00000000..9ce4e8f3 --- /dev/null +++ b/src/handlers/get-shared-link-by-id.js @@ -0,0 +1,29 @@ +const { processRequest, processResponse } = require("./middleware"); +const { getSharedLink } = require("../api/opensearch"); +const opensearchResponse = require("../api/response/opensearch"); + +/** + * Get a shared link document by id + */ +exports.handler = async (event) => { + event = processRequest(event); + const id = event.pathParameters.id; + const esResponse = await getSharedLink(id); + if (linkExpired(esResponse)) { + return { + statusCode: 404, + headers: { "content-type": "text/plain" }, + body: "Not Found", + }; + } else { + const response = opensearchResponse.transform(esResponse); + return processResponse(event, response); + } +}; + +const linkExpired = (response) => { + const body = JSON.parse(response.body); + const expires = new Date(body?._source?.expires); + + return expires <= new Date(); +}; diff --git a/template.yaml b/template.yaml index b0c2fb51..f1b01af3 100644 --- a/template.yaml +++ b/template.yaml @@ -371,6 +371,36 @@ Resources: ApiId: !Ref dcApi Path: /{proxy+} Method: OPTIONS + getSharedLinkByIdFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/get-shared-link-by-id.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Gets a shared link document by id. + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + ENV_PREFIX: !Ref EnvironmentPrefix + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /resolve/{id} + Method: GET dcApi: Type: AWS::Serverless::HttpApi Properties: diff --git a/test/fixtures/mocks/expired-shared-link-9101112.json b/test/fixtures/mocks/expired-shared-link-9101112.json new file mode 100644 index 00000000..27fd1219 --- /dev/null +++ b/test/fixtures/mocks/expired-shared-link-9101112.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-shared_links", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "target_index": "meadow", + "target_id": "23255308-53f4-4c96-a268-aefa596d9d21", + "shared_link_id": "b47a0a1c-ca52-424f-877f-a2ab9f1c0735", + "expires": "2021-10-26T16:47:06Z" + } +} diff --git a/test/fixtures/mocks/missing-shared-link-5678.json b/test/fixtures/mocks/missing-shared-link-5678.json new file mode 100644 index 00000000..f6bf85fb --- /dev/null +++ b/test/fixtures/mocks/missing-shared-link-5678.json @@ -0,0 +1,6 @@ +{ + "_index": "dev-shared_links", + "_type": "_doc", + "_id": "1234", + "found": false +} diff --git a/test/fixtures/mocks/shared-link-1234.json b/test/fixtures/mocks/shared-link-1234.json new file mode 100644 index 00000000..c2838092 --- /dev/null +++ b/test/fixtures/mocks/shared-link-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-shared_links", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "target_index": "meadow", + "target_id": "23255308-53f4-4c96-a268-aefa596d9d21", + "shared_link_id": "b47a0a1c-ca52-424f-877f-a2ab9f1c0735", + "expires": "2028-10-26T16:47:06Z" + } +} diff --git a/test/integration/get-shared-link-by-id.test.js b/test/integration/get-shared-link-by-id.test.js new file mode 100644 index 00000000..d137e547 --- /dev/null +++ b/test/integration/get-shared-link-by-id.test.js @@ -0,0 +1,64 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; + +describe("Retrieve shared link by id", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /resolve/{id}", () => { + const { handler } = require("../../src/handlers/get-shared-link-by-id"); + + it("retrieves a single shared link document", async () => { + mock + .get("/shared_links/_doc/1234") + .reply(200, helpers.testFixture("mocks/shared-link-1234.json")); + + const event = helpers + .mockEvent("GET", "/resolve/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.target_id).to.eq( + "23255308-53f4-4c96-a268-aefa596d9d21" + ); + }); + + it("404s a missing shared link", async () => { + mock + .get("/shared_links/_doc/5678") + .reply(404, helpers.testFixture("mocks/missing-shared-link-5678.json")); + + const event = helpers + .mockEvent("GET", "/shared_links/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 5678 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("404s an expired shared link", async () => { + mock + .get("/shared_links/_doc/9101112") + .reply( + 200, + helpers.testFixture("mocks/expired-shared-link-9101112.json") + ); + + const event = helpers + .mockEvent("GET", "/shared_links/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 9101112 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + }); +}); From b44e1dff48df7370328fd25c0cbe93f1a676cf4e Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 13 Oct 2022 16:39:47 +0000 Subject: [PATCH 29/61] Add automatic deploy on push to deployable branch --- .github/workflows/{docs.yml => deploy.yml} | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) rename .github/workflows/{docs.yml => deploy.yml} (64%) diff --git a/.github/workflows/docs.yml b/.github/workflows/deploy.yml similarity index 64% rename from .github/workflows/docs.yml rename to .github/workflows/deploy.yml index d6651338..db94c26f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/deploy.yml @@ -1,10 +1,35 @@ -name: Generate Docs +name: Deploy on: push: branches: - deploy/staging - main +env: + CONFIG_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} jobs: + build-deploy: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} + steps: + - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + repository: "nulib/tfvars" + ref: main + path: ".tfvars" + token: ${{ secrets.NULIB_ACCESS_TOKEN }} + - uses: actions/setup-python@v2 + - uses: aws-actions/setup-sam@v1 + - uses: aws-actions/configure-aws-credentials@master + with: + role-to-assume: arn:aws:iam::${{ secrets.AwsAccount }}:role/github-actions-role + aws-region: us-east-1 + - run: ln -s .tfvars/dc-api/samconfig.toml . + - run: sam build + - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --config-env $CONFIG_ENV docs-changed: runs-on: ubuntu-latest outputs: @@ -27,8 +52,7 @@ jobs: permissions: id-token: write contents: read - environment: - name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} + environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@master From 7cad80dfa7c1896e0a0e8a8df671665f6104f62b Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Fri, 14 Oct 2022 17:24:27 +0000 Subject: [PATCH 30/61] Return a collection as IIIF collection --- src/handlers/get-collection-by-id.js | 14 ++++- test/fixtures/mocks/collection-1234.json | 1 + test/integration/get-collection-by-id.js | 75 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 test/integration/get-collection-by-id.js diff --git a/src/handlers/get-collection-by-id.js b/src/handlers/get-collection-by-id.js index e5e6754e..1e55b368 100644 --- a/src/handlers/get-collection-by-id.js +++ b/src/handlers/get-collection-by-id.js @@ -1,14 +1,26 @@ const { processRequest, processResponse } = require("./middleware"); const { getCollection } = require("../api/opensearch"); +const { getSearch } = require("./search"); const opensearchResponse = require("../api/response/opensearch"); /** - * A simple function to get a Collection by id + * Get a colletion by id */ exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; const esResponse = await getCollection(id); + + const collection = JSON.parse(esResponse.body)?._source; + + if (event.queryStringParameters?.as === "iiif" && collection) { + event.queryStringParameters.query = `collection.id:${id}`; + event.queryStringParameters.collectionLabel = collection?.title || ""; + event.queryStringParameters.collectionSummary = + collection?.description || ""; + return await getSearch(event); + } + const response = opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/test/fixtures/mocks/collection-1234.json b/test/fixtures/mocks/collection-1234.json index 6dbe04cb..f29db15d 100644 --- a/test/fixtures/mocks/collection-1234.json +++ b/test/fixtures/mocks/collection-1234.json @@ -6,6 +6,7 @@ "found": true, "_source": { "id": "1234", + "title": "Collection Title", "api_model": "Collection", "published": true, "representative_image": { diff --git a/test/integration/get-collection-by-id.js b/test/integration/get-collection-by-id.js new file mode 100644 index 00000000..11630d6c --- /dev/null +++ b/test/integration/get-collection-by-id.js @@ -0,0 +1,75 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const RequestPipeline = require("../../src/api/request/pipeline"); + +describe("Retrieve collection by id", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /collections/{id}", () => { + const { handler } = require("../../src/handlers/get-collection-by-id"); + + it("retrieves a single collection link document", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + + const event = helpers + .mockEvent("GET", "/collections/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.id).to.eq("1234"); + }); + + it("404s a missing collection", async () => { + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-collection-1234.json")); + + const event = helpers + .mockEvent("GET", "/collections/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("returns a single collection as a IIIF collection", async () => { + const originalQuery = { + query: { query_string: { query: "collection.id:1234" } }, + }; + const authQuery = new RequestPipeline(originalQuery) + .authFilter() + .toJson(); + + mock + .get("/dc-v2-collection/_doc/1234") + .reply(200, helpers.testFixture("mocks/collection-1234.json")); + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + + const event = helpers + .mockEvent("GET", "/collections/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .queryParams({ as: "iiif" }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + const resultBody = JSON.parse(result.body); + expect(resultBody.type).to.eq("Collection"); + expect(resultBody.label.none[0]).to.eq("Collection Title"); + }); + }); +}); From 0b0512c5313f40edb6dce5e347871112f63f078f Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Fri, 14 Oct 2022 20:27:38 +0000 Subject: [PATCH 31/61] Allow certain IPs to receive private results Bring helpers.js up to 100% test coverage Eliminate redundancy in template.yml using a global function definition --- src/api/opensearch.js | 31 +++-- src/api/request/pipeline.js | 27 +++-- src/handlers/get-collections.js | 2 +- src/handlers/get-file-set-by-id.js | 4 +- src/handlers/get-work-by-id.js | 4 +- src/handlers/search.js | 3 +- src/helpers.js | 7 ++ template.yaml | 133 +++------------------ test/fixtures/mocks/private-work-1234.json | 13 ++ test/integration/get-collections.test.js | 9 +- test/integration/get-doc.test.js | 29 +++++ test/test-helpers/event-builder.js | 7 +- test/unit/api/helpers.test.js | 35 ++++++ test/unit/api/request/pipeline.test.js | 46 +++++-- 14 files changed, 184 insertions(+), 166 deletions(-) create mode 100644 test/fixtures/mocks/private-work-1234.json diff --git a/src/api/opensearch.js b/src/api/opensearch.js index f8b52e01..6bf3af3a 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -2,28 +2,28 @@ const { HttpRequest } = require("@aws-sdk/protocol-http"); const { awsFetch } = require("../aws/fetch"); const { elasticsearchEndpoint, prefix } = require("../aws/environment"); -async function getCollection(id) { - return getDocument("dc-v2-collection", id); +async function getCollection(id, opts) { + return getDocument("dc-v2-collection", id, opts); } -async function getFileSet(id) { - return getDocument("dc-v2-file-set", id); +async function getFileSet(id, opts) { + return getDocument("dc-v2-file-set", id, opts); } -async function getWork(id) { - return getDocument("dc-v2-work", id); +async function getWork(id, opts) { + return getDocument("dc-v2-work", id, opts); } -async function getSharedLink(id) { - return getDocument("shared_links", id); +async function getSharedLink(id, opts) { + return getDocument("shared_links", id, opts); } -async function getDocument(index, id) { +async function getDocument(index, id, opts) { const request = initRequest(`/${prefix(index)}/_doc/${id}`); let response = await awsFetch(request); if (response.statusCode === 200) { const body = JSON.parse(response.body); - if (index != "shared_links" && !isVisible(body)) { + if (index != "shared_links" && !isVisible(body, opts?.allowPrivate)) { let responseBody = { _index: prefix(index), _type: "_doc", @@ -40,14 +40,13 @@ async function getDocument(index, id) { return response; } -function isVisible(doc) { - if (!doc?.found) { - return false; - } +function isVisible(doc, allowPrivate) { + if (!doc?.found) return false; + const isAllowed = allowPrivate || doc?._source?.visibility !== "Private"; if (doc?._source.api_model == "FileSet") { - return doc?._source?.visibility !== "Private"; + return isAllowed; } else { - return doc?._source?.published && doc?._source?.visibility !== "Private"; + return doc?._source?.published && isAllowed; } } diff --git a/src/api/request/pipeline.js b/src/api/request/pipeline.js index c825e57b..06c1f6a8 100644 --- a/src/api/request/pipeline.js +++ b/src/api/request/pipeline.js @@ -1,3 +1,17 @@ +const { isFromReadingRoom } = require("../../helpers"); + +function filterFor(query, event) { + const matchTheQuery = query; + const beUnpublished = { term: { published: false } }; + const beRestricted = { term: { visibility: "Private" } }; + + const filter = isFromReadingRoom(event) + ? { must: [matchTheQuery], must_not: [beUnpublished] } + : { must: [matchTheQuery], must_not: [beUnpublished, beRestricted] }; + + return { bool: filter }; +} + module.exports = class RequestPipeline { constructor(searchContext) { this.searchContext = { ...searchContext }; @@ -9,17 +23,8 @@ module.exports = class RequestPipeline { // - Reading room/IP (not in first iteration) // - Add `track_total_hits` to search context (so we can get accurate hits.total.value) - authFilter() { - const matchTheQuery = this.searchContext.query; - const beUnpublished = { term: { published: false } }; - const beRestricted = { term: { visibility: "Private" } }; - - this.searchContext.query = { - bool: { - must: [matchTheQuery], - must_not: [beUnpublished, beRestricted], - }, - }; + authFilter(event) { + this.searchContext.query = filterFor(this.searchContext.query, event); this.searchContext.track_total_hits = true; return this; diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index c9018a5d..51a1971d 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -37,7 +37,7 @@ exports.handler = async (event) => { null, { extraParams, includeToken: false } ); - const filteredBody = new RequestPipeline(body).authFilter().toJson(); + const filteredBody = new RequestPipeline(body).authFilter(event).toJson(); let esResponse = await search(modelsToTargets(models), filteredBody); let transformedResponse = await opensearchResponse.transform( esResponse, diff --git a/src/handlers/get-file-set-by-id.js b/src/handlers/get-file-set-by-id.js index fc89cf6d..f54a3a70 100644 --- a/src/handlers/get-file-set-by-id.js +++ b/src/handlers/get-file-set-by-id.js @@ -1,5 +1,6 @@ const { processRequest, processResponse } = require("./middleware"); const { getFileSet } = require("../api/opensearch"); +const { isFromReadingRoom } = require("../helpers"); const opensearchResponse = require("../api/response/opensearch"); /** @@ -8,7 +9,8 @@ const opensearchResponse = require("../api/response/opensearch"); exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; - const esResponse = await getFileSet(id); + const allowPrivate = isFromReadingRoom(event); + const esResponse = await getFileSet(id, { allowPrivate }); const response = opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/src/handlers/get-work-by-id.js b/src/handlers/get-work-by-id.js index bfb3be2f..68c16c56 100644 --- a/src/handlers/get-work-by-id.js +++ b/src/handlers/get-work-by-id.js @@ -1,5 +1,6 @@ const { processRequest, processResponse } = require("./middleware"); const { getWork } = require("../api/opensearch"); +const { isFromReadingRoom } = require("../helpers"); const opensearchResponse = require("../api/response/opensearch"); /** @@ -8,7 +9,8 @@ const opensearchResponse = require("../api/response/opensearch"); exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; - const esResponse = await getWork(id); + const allowPrivate = isFromReadingRoom(event); + const esResponse = await getWork(id, { allowPrivate }); const response = opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/src/handlers/search.js b/src/handlers/search.js index d4d71aa3..df29def9 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -12,7 +12,6 @@ const RequestPipeline = require("../api/request/pipeline"); const getSearch = async (event) => { event = processRequest(event); - const models = extractRequestedModels(event.pathParameters?.models); const format = await responseFormat(event); @@ -67,7 +66,7 @@ const executeSearch = async ( options ); const filteredSearchContext = new RequestPipeline(searchContext) - .authFilter() + .authFilter(event) .toJson(); const esResponse = await search( modelsToTargets(models), diff --git a/src/helpers.js b/src/helpers.js index cbb13e9b..1d4035b5 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -96,10 +96,17 @@ function objectifyCookies(event) { return event; } +function isFromReadingRoom(event) { + const AllowedIPs = (process.env.READING_ROOM_IPS || "").split(/\s*,\s*/); + const sourceIp = event?.requestContext?.http?.sourceIp; + return AllowedIPs.includes(sourceIp); +} + module.exports = { addCorsHeaders, baseUrl, decodeEventBody, + isFromReadingRoom, normalizeHeaders, objectifyCookies, }; diff --git a/template.yaml b/template.yaml index f1b01af3..f01d2be0 100644 --- a/template.yaml +++ b/template.yaml @@ -4,12 +4,22 @@ Description: > dc-api-v2 Sample SAM Template for dc-api-v2 - # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: + CodeUri: ./src + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 Timeout: 3 - + Environment: + Variables: + DC_API_ENDPOINT: !Ref DcApiEndpoint + DC_URL: !Ref DcUrl + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + ENV_PREFIX: !Ref EnvironmentPrefix + READING_ROOM_IPS: !Ref ReadingRoomIPs Parameters: ApiSecret: Type: String @@ -42,6 +52,9 @@ Parameters: NussoBaseUrl: Type: String Description: Auth server URL + ReadingRoomIPs: + Type: String + Description: Comma-delimited list of IP addresses to serve private resources to V1ApiId: Type: String Description: ID of the v1 API to mount on /api/v1 @@ -54,19 +67,11 @@ Resources: getAuthCallbackFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-auth-callback.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: NUSSO callback function. Environment: Variables: API_TOKEN_SECRET: !Ref ApiSecret - DC_API_ENDPOINT: !Ref DcApiEndpoint - DC_URL: !Ref DcUrl NUSSO_API_KEY: !Ref NussoApiKey NUSSO_BASE_URL: !Ref NussoBaseUrl Events: @@ -79,18 +84,10 @@ Resources: getAuthLoginFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-auth-login.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Performs NUSSO login. Environment: Variables: - DC_API_ENDPOINT: !Ref DcApiEndpoint - DC_URL: !Ref DcUrl NUSSO_API_KEY: !Ref NussoApiKey NUSSO_BASE_URL: !Ref NussoBaseUrl Events: @@ -103,19 +100,11 @@ Resources: getAuthWhoAmIFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-auth-whoami.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Exchanges valid JWT token for user information. Environment: Variables: API_TOKEN_SECRET: !Ref ApiSecret - DC_API_ENDPOINT: !Ref DcApiEndpoint - DC_URL: !Ref DcUrl NUSSO_API_KEY: !Ref NussoApiKey NUSSO_BASE_URL: !Ref NussoBaseUrl Events: @@ -128,13 +117,7 @@ Resources: getCollectionsFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-collections.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets Collections. Policies: Version: 2012-10-17 @@ -144,11 +127,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - DC_URL: !Ref DcUrl - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: Type: HttpApi @@ -159,13 +137,7 @@ Resources: getCollectionByIdFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-collection-by-id.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets a Collection by id. Policies: Version: 2012-10-17 @@ -175,11 +147,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - DC_URL: !Ref DcUrl - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: Type: HttpApi @@ -190,13 +157,7 @@ Resources: getFileSetByIdFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-file-set-by-id.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets a FileSet by id. Policies: Version: 2012-10-17 @@ -206,11 +167,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - DC_URL: !Ref DcUrl - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: Type: HttpApi @@ -221,13 +177,7 @@ Resources: getWorkByIdFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-work-by-id.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets a Work by id. Policies: Version: 2012-10-17 @@ -237,11 +187,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - DC_URL: !Ref DcUrl - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: Type: HttpApi @@ -252,13 +197,7 @@ Resources: getThumbnailFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-thumbnail.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets a Work's representative thumbnail. Policies: Version: 2012-10-17 @@ -271,8 +210,6 @@ Resources: Environment: Variables: API_TOKEN_SECRET: !Ref ApiSecret - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: CollectionApi: Type: HttpApi @@ -289,13 +226,7 @@ Resources: searchPostFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/search.postSearch - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Handles OpenSearch search requests, Works only by default. Policies: Version: 2012-10-17 @@ -305,10 +236,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: SearchApi: Type: HttpApi @@ -325,13 +252,7 @@ Resources: searchGetFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/search.getSearch - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Handles paging requests Policies: Version: 2012-10-17 @@ -341,11 +262,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - DC_URL: !Ref DcUrl - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: SearchApi: Type: HttpApi @@ -356,12 +272,7 @@ Resources: optionsFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/options-request.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 Timeout: 3 Description: Handles all OPTIONS requests Events: @@ -374,13 +285,7 @@ Resources: getSharedLinkByIdFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-shared-link-by-id.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 - Timeout: 100 Description: Gets a shared link document by id. Policies: Version: 2012-10-17 @@ -390,10 +295,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: Api: Type: HttpApi @@ -433,10 +334,6 @@ Resources: Properties: CodeUri: ./redirect Handler: index.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 Timeout: 1 Description: Redirects to latest version of docs Environment: diff --git a/test/fixtures/mocks/private-work-1234.json b/test/fixtures/mocks/private-work-1234.json new file mode 100644 index 00000000..05573d3e --- /dev/null +++ b/test/fixtures/mocks/private-work-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": true, + "visibility": "Private" + } +} diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 4bec07bb..46ff442f 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -14,12 +14,13 @@ describe("Collections route", () => { const baseEvent = helpers .mockEvent("GET", "/collections") .pathPrefix("/api/v2"); + let event; const makeQuery = (params) => new RequestPipeline(params).authFilter().toJson(); describe("validates parameters", () => { it("page", async () => { - const event = baseEvent.queryParams({ page: 0 }).render(); + event = baseEvent.queryParams({ page: 0 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(400); const resultBody = JSON.parse(result.body); @@ -27,7 +28,7 @@ describe("Collections route", () => { }); it("size", async () => { - const event = baseEvent.queryParams({ size: 0 }).render(); + event = baseEvent.queryParams({ size: 0 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(400); const resultBody = JSON.parse(result.body); @@ -39,7 +40,7 @@ describe("Collections route", () => { mock .post("/dc-v2-collection/_search", makeQuery({ size: 10, from: 0 })) .reply(200, helpers.testFixture("mocks/collections.json")); - const event = baseEvent.render(); + event = baseEvent.render(); const result = await handler(event); expect(result.statusCode).to.eq(200); expect(result.headers).to.include({ "content-type": "application/json" }); @@ -55,7 +56,7 @@ describe("Collections route", () => { mock .post("/dc-v2-collection/_search", makeQuery({ size: 5, from: 10 })) .reply(200, helpers.testFixture("mocks/collections.json")); - const event = baseEvent.queryParams({ page: 3, size: 5 }).render(); + event = baseEvent.queryParams({ page: 3, size: 5 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(200); expect(result.headers).to.include({ "content-type": "application/json" }); diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js index 0f0d2905..938357dc 100644 --- a/test/integration/get-doc.test.js +++ b/test/integration/get-doc.test.js @@ -56,6 +56,35 @@ describe("Doc retrieval routes", () => { const result = await handler(event); expect(result.statusCode).to.eq(404); }); + + it("404s a private work by default", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/private-work-1234.json")); + + const event = helpers + .mockEvent("GET", "/works/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("returns a private work to allowed IPs", async () => { + process.env.READING_ROOM_IPS = "127.0.0.1"; + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/private-work-1234.json")); + + const event = helpers + .mockEvent("GET", "/works/{id}") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + }); }); describe("GET /collections/{id}", () => { diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index 15ccf742..76942b7e 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -9,7 +9,6 @@ module.exports = class { routeKey: `${method} ${route}`, rawPath: route, rawQueryString: "", - cookies: [], headers: { Host: "api.test.library.northwestern.edu", "X-Forwarded-For": "127.0.0.1, 127.0.0.2", @@ -87,6 +86,12 @@ module.exports = class { return this; } + cookie(name, value) { + if (!this._event.cookies) this._event.cookies = []; + this._event.cookies.push(`${name}=${encodeURIComponent(value)}`); + return this; + } + render() { const result = { ...this._event }; diff --git a/test/unit/api/helpers.test.js b/test/unit/api/helpers.test.js index 13a427f1..11d04bef 100644 --- a/test/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -3,7 +3,9 @@ const { baseUrl, decodeEventBody, + isFromReadingRoom, normalizeHeaders, + objectifyCookies, } = require("../../../src/helpers"); const chai = require("chai"); const expect = chai.expect; @@ -151,6 +153,17 @@ describe("helpers", () => { }); }); + describe("isFromReadingRoom()", () => { + helpers.saveEnvironment(); + + it("knows when a request is coming from a reading room IP", () => { + const event = helpers.mockEvent("GET", "/search").render(); + expect(isFromReadingRoom(event)).to.be.false; + process.env.READING_ROOM_IPS = event.requestContext.http.sourceIp; + expect(isFromReadingRoom(event)).to.be.true; + }); + }); + describe("normalizeHeaders()", () => { it("converts all headers to lowercase", () => { const upperHeaders = ["Host", "X-Forwarded-For", "X-Forwarded-Proto"]; @@ -165,4 +178,26 @@ describe("helpers", () => { expect(result.headers).not.to.include.keys(upperHeaders); }); }); + + describe("objectifyCookies", () => { + it("works when there are no cookies", () => { + const event = helpers.mockEvent("GET", "/search").render(); + const result = objectifyCookies(event); + expect(result.cookies).to.be.undefined; + expect(result.cookieObject).to.be.empty; + }); + + it("works when there are cookies", () => { + const event = helpers + .mockEvent("GET", "/search") + .cookie("testName", "works when there are cookies") + .cookie("cookieType", "snickerdoodle") + .render(); + const result = objectifyCookies(event); + expect(result.cookieObject).to.include({ + testName: "works%20when%20there%20are%20cookies", + cookieType: "snickerdoodle", + }); + }); + }); }); diff --git a/test/unit/api/request/pipeline.test.js b/test/unit/api/request/pipeline.test.js index a038fe12..5f023df7 100644 --- a/test/unit/api/request/pipeline.test.js +++ b/test/unit/api/request/pipeline.test.js @@ -5,6 +5,10 @@ const chai = require("chai"); const expect = chai.expect; describe("RequestPipeline", () => { + helpers.saveEnvironment(); + + const event = helpers.mockEvent("GET", "/search").render(); + const requestBody = { query: { match: { term: { title: "The Title" } } }, size: 50, @@ -18,18 +22,8 @@ describe("RequestPipeline", () => { pipeline = new RequestPipeline(requestBody); }); - it("adds an auth filter", () => { - const result = pipeline.authFilter(); - expect(result.searchContext.size).to.eq(50); - expect(result.searchContext.query.bool.must).to.include(requestBody.query); - expect(result.searchContext.query.bool.must_not).to.deep.include( - { term: { visibility: "Private" } }, - { term: { published: false } } - ); - }); - it("sets a default query", () => { - const result = new RequestPipeline({ size: 20 }).authFilter(); + const result = new RequestPipeline({ size: 20 }).authFilter(event); expect(result.searchContext.size).to.eq(20); expect(result.searchContext.query.bool.must).to.deep.include({ match_all: {}, @@ -39,4 +33,34 @@ describe("RequestPipeline", () => { it("serializes JSON", () => { expect(JSON.parse(pipeline.toJson())).to.deep.equal(requestBody); }); + + describe("reading room IPs", () => { + it("filters out private results by default", () => { + process.env.READING_ROOM_IPS = "192.168.0.1,172.16.10.2"; + const result = pipeline.authFilter(event); + expect(result.searchContext.size).to.eq(50); + expect(result.searchContext.query.bool.must).to.include( + requestBody.query + ); + expect(result.searchContext.query.bool.must_not).to.deep.include( + { term: { visibility: "Private" } }, + { term: { published: false } } + ); + }); + + it("includes private results from allowed IP addresses", () => { + process.env.READING_ROOM_IPS = "127.0.0.1,192.168.0.1"; + const result = pipeline.authFilter(event); + expect(result.searchContext.size).to.eq(50); + expect(result.searchContext.query.bool.must).to.include( + requestBody.query + ); + expect(result.searchContext.query.bool.must_not).to.deep.include({ + term: { published: false }, + }); + expect(result.searchContext.query.bool.must_not).not.to.deep.include({ + term: { visibility: "Private" }, + }); + }); + }); }); From 0c70a0cf0885049836834073ffadec3620428028 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 17 Oct 2022 23:22:41 +0000 Subject: [PATCH 32/61] Use repo deploy key instead of personal access token --- .github/workflows/deploy.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index db94c26f..2c36a3b5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,13 +14,16 @@ jobs: contents: read environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} steps: + - name: Set GitHub Deploy Key + uses: webfactory/ssh-agent@v0.5.3 + with: + ssh-private-key: ${{ secrets.TFVARS_DEPLOY_KEY }} - uses: actions/checkout@v2 - uses: actions/checkout@v2 with: repository: "nulib/tfvars" ref: main path: ".tfvars" - token: ${{ secrets.NULIB_ACCESS_TOKEN }} - uses: actions/setup-python@v2 - uses: aws-actions/setup-sam@v1 - uses: aws-actions/configure-aws-credentials@master From becf561687e8de0d51ca4dd2699cd355cbde9e0d Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Fri, 14 Oct 2022 14:15:38 +0000 Subject: [PATCH 33/61] Adds getSimilar handler function --- package-lock.json | 26 -- src/handlers/get-similar.js | 84 ++++++ template.yaml | 31 +++ test/fixtures/mocks/similar.json | 379 +++++++++++++++++++++++++++ test/integration/get-similar.test.js | 124 +++++++++ 5 files changed, 618 insertions(+), 26 deletions(-) create mode 100644 src/handlers/get-similar.js create mode 100644 test/fixtures/mocks/similar.json create mode 100644 test/integration/get-similar.test.js diff --git a/package-lock.json b/package-lock.json index cbcb5974..063cc087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1320,25 +1320,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/binary-extensions": { "version": "2.2.0", "dev": true, @@ -3342,7 +3323,6 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", - "base64-js": "^1.5.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" @@ -4259,11 +4239,6 @@ "version": "1.0.2", "dev": true }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, "binary-extensions": { "version": "2.2.0", "dev": true @@ -4445,7 +4420,6 @@ "@aws-sdk/protocol-http": "^3.127.0", "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", - "base64-js": "^1.5.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4" diff --git a/src/handlers/get-similar.js b/src/handlers/get-similar.js new file mode 100644 index 00000000..12bcf295 --- /dev/null +++ b/src/handlers/get-similar.js @@ -0,0 +1,84 @@ +const { baseUrl } = require("../helpers"); +const { modelsToTargets } = require("../api/request/models"); +const { search } = require("../api/opensearch"); +const opensearchResponse = require("../api/response/opensearch"); +const { Paginator } = require("../api/pagination"); +const { processRequest, processResponse } = require("./middleware"); +const RequestPipeline = require("../api/request/pipeline"); + +const DefaultSize = 5; + +const numberParam = (value, defaultValue) => { + const result = Number(value); + if (isNaN(result)) return defaultValue; + return result; +}; + +/** + * Get similar works via 'More Like This' query + */ +exports.handler = async (event) => { + event = processRequest(event); + + const id = event.pathParameters.id; + const page = numberParam(event.queryStringParameters?.page, 1); + if (page < 1) return invalidRequest("page must be >= 1"); + const size = numberParam(event.queryStringParameters?.size, DefaultSize); + if (size < 1) return invalidRequest("size must be >= 1"); + + const models = ["works"]; + const workIndex = modelsToTargets(models); + + let body = { + size: size, + from: size * (page - 1), + query: { + more_like_this: { + fields: [ + "title", + "description", + "subject.label", + "genre.label", + "contributor.label", + "creator.label", + ], + like: [ + { + _index: workIndex, + _id: id, + }, + ], + max_query_terms: 10, + min_doc_freq: 1, + min_term_freq: 1, + }, + }, + }; + + const extraParams = size === DefaultSize ? {} : { size }; + const pager = new Paginator( + baseUrl(event), + `works/${id}/similar`, + models, + body, + null, + { + extraParams, + includeToken: false, + } + ); + const filteredBody = new RequestPipeline(body).authFilter().toJson(); + let esResponse = await search(workIndex, filteredBody); + let transformedResponse = await opensearchResponse.transform( + esResponse, + pager + ); + return processResponse(event, transformedResponse); +}; + +const invalidRequest = (message) => { + return { + statusCode: 400, + body: JSON.stringify({ message: message }), + }; +}; diff --git a/template.yaml b/template.yaml index f01d2be0..93e4d511 100644 --- a/template.yaml +++ b/template.yaml @@ -223,6 +223,37 @@ Resources: ApiId: !Ref dcApi Path: /works/{id}/thumbnail Method: GET + getSimilarFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: handlers/get-similar.handler + Runtime: nodejs16.x + Architectures: + - x86_64 + MemorySize: 128 + Timeout: 100 + Description: Gets works similar to a specific work. + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + API_TOKEN_SECRET: !Ref ApiSecret + ENV_PREFIX: !Ref EnvironmentPrefix + ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint + Events: + WorkApi: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /works/{id}/similar + Method: GET searchPostFunction: Type: AWS::Serverless::Function Properties: diff --git a/test/fixtures/mocks/similar.json b/test/fixtures/mocks/similar.json new file mode 100644 index 00000000..da89cfa6 --- /dev/null +++ b/test/fixtures/mocks/similar.json @@ -0,0 +1,379 @@ +{ + "took": 2, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 9, + "relation": "eq" + }, + "max_score": 0.52916515, + "hits": [ + { + "_index": "bmq-dev-dc-v2-work-1665757331985", + "_type": "_doc", + "_id": "59c40013-4a18-4bdb-b910-af30dc57bd3e", + "_score": 0.52916515, + "_source": { + "accession_number": "6wYHF8CUMS", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/59c40013-4a18-4bdb-b910-af30dc57bd3e/thumbnail", + "indexed_at": "2022-10-14T16:35:16.201826", + "iiif_manifest": "https://bmq-dev-pyramids.s3.amazonaws.com/public/59/c4/00/13/-4/a1/8-/4b/db/-b/91/0-/af/30/dc/57/bd/3e-manifest.json", + "folder_names": [], + "modified_date": "2022-10-14T16:35:15.877751Z", + "table_of_contents": [], + "source": [], + "provenance": [], + "batch_ids": [ + "4426840e-11c9-4a95-b3ba-8d37ceaf4982", + "d31ec9b1-7110-4828-96c0-f6f4a4247dd1" + ], + "publisher": [], + "date_created": [], + "csv_metadata_update_jobs": [], + "keywords": [], + "box_name": [], + "id": "59c40013-4a18-4bdb-b910-af30dc57bd3e", + "caption": [], + "box_number": [], + "create_date": "2022-10-14T14:29:10.804337Z", + "terms_of_use": null, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "preservation_level": null, + "creator": [], + "rights_holder": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/59c40013-4a18-4bdb-b910-af30dc57bd3e", + "representative_file_set": { + "id": null, + "url": null + }, + "abstract": [], + "subject": [], + "physical_description_material": [], + "published": true, + "alternate_title": [], + "collection": {}, + "physical_description_size": [], + "notes": [], + "legacy_identifier": [], + "description": [ + "This funky description contains the word funky twice in it!" + ], + "rights_statement": {}, + "title": "Example work one", + "identifier": [], + "file_sets": [], + "ark": "ark:/99999/fk474661182", + "related_material": [], + "library_unit": null, + "work_type": "Image", + "contributor": [], + "license": null, + "series": [], + "catalog_key": [], + "status": null, + "scope_and_contents": [], + "related_url": [] + } + }, + { + "_index": "bmq-dev-dc-v2-work-1665757331985", + "_type": "_doc", + "_id": "6d17e419-f318-433e-9dd4-b9229aa4e860", + "_score": 0.52916515, + "_source": { + "accession_number": "5UcXW7p6Wr", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/6d17e419-f318-433e-9dd4-b9229aa4e860/thumbnail", + "indexed_at": "2022-10-14T16:35:16.201941", + "iiif_manifest": "https://bmq-dev-pyramids.s3.amazonaws.com/public/6d/17/e4/19/-f/31/8-/43/3e/-9/dd/4-/b9/22/9a/a4/e8/60-manifest.json", + "folder_names": [], + "modified_date": "2022-10-14T16:35:15.877751Z", + "table_of_contents": [], + "source": [], + "provenance": [], + "batch_ids": [ + "4426840e-11c9-4a95-b3ba-8d37ceaf4982", + "d31ec9b1-7110-4828-96c0-f6f4a4247dd1" + ], + "publisher": [], + "date_created": [], + "csv_metadata_update_jobs": [], + "keywords": [], + "box_name": [], + "id": "6d17e419-f318-433e-9dd4-b9229aa4e860", + "caption": [], + "box_number": [], + "create_date": "2022-10-14T14:36:16.319729Z", + "terms_of_use": null, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "preservation_level": null, + "creator": [], + "rights_holder": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/6d17e419-f318-433e-9dd4-b9229aa4e860", + "representative_file_set": { + "id": null, + "url": null + }, + "abstract": [], + "subject": [], + "physical_description_material": [], + "published": true, + "alternate_title": [], + "collection": {}, + "physical_description_size": [], + "notes": [], + "legacy_identifier": [], + "description": [ + "This funky description contains the word funky twice in it!" + ], + "rights_statement": {}, + "title": "Example work ten", + "identifier": [], + "file_sets": [], + "ark": "ark:/99999/fk460296587", + "related_material": [], + "library_unit": null, + "work_type": "Image", + "contributor": [], + "license": null, + "series": [], + "catalog_key": [], + "status": null, + "scope_and_contents": [], + "related_url": [] + } + }, + { + "_index": "bmq-dev-dc-v2-work-1665757331985", + "_type": "_doc", + "_id": "7aefc9f1-e796-4a5c-ab00-29444743fe19", + "_score": 0.52916515, + "_source": { + "accession_number": "S4cfHEOHtu", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/7aefc9f1-e796-4a5c-ab00-29444743fe19/thumbnail", + "indexed_at": "2022-10-14T16:35:16.202029", + "iiif_manifest": "https://bmq-dev-pyramids.s3.amazonaws.com/public/7a/ef/c9/f1/-e/79/6-/4a/5c/-a/b0/0-/29/44/47/43/fe/19-manifest.json", + "folder_names": [], + "modified_date": "2022-10-14T16:35:15.877751Z", + "table_of_contents": [], + "source": [], + "provenance": [], + "batch_ids": [ + "d31ec9b1-7110-4828-96c0-f6f4a4247dd1", + "4426840e-11c9-4a95-b3ba-8d37ceaf4982" + ], + "publisher": [], + "date_created": [], + "csv_metadata_update_jobs": [], + "keywords": [], + "box_name": [], + "id": "7aefc9f1-e796-4a5c-ab00-29444743fe19", + "caption": [], + "box_number": [], + "create_date": "2022-10-14T14:29:23.472711Z", + "terms_of_use": null, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "preservation_level": null, + "creator": [], + "rights_holder": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/7aefc9f1-e796-4a5c-ab00-29444743fe19", + "representative_file_set": { + "id": null, + "url": null + }, + "abstract": [], + "subject": [], + "physical_description_material": [], + "published": true, + "alternate_title": [], + "collection": {}, + "physical_description_size": [], + "notes": [], + "legacy_identifier": [], + "description": [ + "This funky description contains the word funky twice in it!" + ], + "rights_statement": {}, + "title": "Example work two", + "identifier": [], + "file_sets": [], + "ark": "ark:/99999/fk450285443", + "related_material": [], + "library_unit": null, + "work_type": "Image", + "contributor": [], + "license": null, + "series": [], + "catalog_key": [], + "status": null, + "scope_and_contents": [], + "related_url": [] + } + }, + { + "_index": "bmq-dev-dc-v2-work-1665757331985", + "_type": "_doc", + "_id": "5eede857-5752-48f8-b801-8597068cd6ac", + "_score": 0.52916515, + "_source": { + "accession_number": "2GdGfSapum", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/5eede857-5752-48f8-b801-8597068cd6ac/thumbnail", + "indexed_at": "2022-10-14T16:35:16.202130", + "iiif_manifest": "https://bmq-dev-pyramids.s3.amazonaws.com/public/5e/ed/e8/57/-5/75/2-/48/f8/-b/80/1-/85/97/06/8c/d6/ac-manifest.json", + "folder_names": [], + "modified_date": "2022-10-14T16:35:15.877751Z", + "table_of_contents": [], + "source": [], + "provenance": [], + "batch_ids": [ + "d31ec9b1-7110-4828-96c0-f6f4a4247dd1", + "4426840e-11c9-4a95-b3ba-8d37ceaf4982" + ], + "publisher": [], + "date_created": [], + "csv_metadata_update_jobs": [], + "keywords": [], + "box_name": [], + "id": "5eede857-5752-48f8-b801-8597068cd6ac", + "caption": [], + "box_number": [], + "create_date": "2022-10-14T14:29:37.259623Z", + "terms_of_use": null, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "preservation_level": null, + "creator": [], + "rights_holder": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/5eede857-5752-48f8-b801-8597068cd6ac", + "representative_file_set": { + "id": null, + "url": null + }, + "abstract": [], + "subject": [], + "physical_description_material": [], + "published": true, + "alternate_title": [], + "collection": {}, + "physical_description_size": [], + "notes": [], + "legacy_identifier": [], + "description": [ + "This funky description contains the word funky twice in it!" + ], + "rights_statement": {}, + "title": "Example work three", + "identifier": [], + "file_sets": [], + "ark": "ark:/99999/fk436474897", + "related_material": [], + "library_unit": null, + "work_type": "Image", + "contributor": [], + "license": null, + "series": [], + "catalog_key": [], + "status": null, + "scope_and_contents": [], + "related_url": [] + } + }, + { + "_index": "bmq-dev-dc-v2-work-1665757331985", + "_type": "_doc", + "_id": "4054f1f0-7e29-4cc8-9884-ae6a141f45d9", + "_score": 0.52916515, + "_source": { + "accession_number": "OVBr2gIugT", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/4054f1f0-7e29-4cc8-9884-ae6a141f45d9/thumbnail", + "indexed_at": "2022-10-14T16:35:16.202201", + "iiif_manifest": "https://bmq-dev-pyramids.s3.amazonaws.com/public/40/54/f1/f0/-7/e2/9-/4c/c8/-9/88/4-/ae/6a/14/1f/45/d9-manifest.json", + "folder_names": [], + "modified_date": "2022-10-14T16:35:15.877751Z", + "table_of_contents": [], + "source": [], + "provenance": [], + "batch_ids": [ + "d31ec9b1-7110-4828-96c0-f6f4a4247dd1", + "4426840e-11c9-4a95-b3ba-8d37ceaf4982" + ], + "publisher": [], + "date_created": [], + "csv_metadata_update_jobs": [], + "keywords": [], + "box_name": [], + "id": "4054f1f0-7e29-4cc8-9884-ae6a141f45d9", + "caption": [], + "box_number": [], + "create_date": "2022-10-14T14:29:52.416654Z", + "terms_of_use": null, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "preservation_level": null, + "creator": [], + "rights_holder": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/4054f1f0-7e29-4cc8-9884-ae6a141f45d9", + "representative_file_set": { + "id": null, + "url": null + }, + "abstract": [], + "subject": [], + "physical_description_material": [], + "published": true, + "alternate_title": [], + "collection": {}, + "physical_description_size": [], + "notes": [], + "legacy_identifier": [], + "description": [ + "This funky description contains the word funky twice in it!" + ], + "rights_statement": {}, + "title": "Example work four", + "identifier": [], + "file_sets": [], + "ark": "ark:/99999/fk463672939", + "related_material": [], + "library_unit": null, + "work_type": "Image", + "contributor": [], + "license": null, + "series": [], + "catalog_key": [], + "status": null, + "scope_and_contents": [], + "related_url": [] + } + } + ] + } +} diff --git a/test/integration/get-similar.test.js b/test/integration/get-similar.test.js new file mode 100644 index 00000000..89d672be --- /dev/null +++ b/test/integration/get-similar.test.js @@ -0,0 +1,124 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const { handler } = require("../../src/handlers/get-similar"); +const RequestPipeline = require("../../src/api/request/pipeline"); + +describe("Similar routes", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + let baseEvent = helpers + .mockEvent("GET", "/works/{id}/similar") + .pathPrefix("/api/v2") + .pathParams({ id: 1234 }); + const makeQuery = (params) => + new RequestPipeline(params).authFilter().toJson(); + + describe("validates parameters", () => { + it("page", async () => { + const event = baseEvent.queryParams({ page: 0 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("page must be >= 1"); + }); + + it("size", async () => { + const event = baseEvent.queryParams({ size: 0 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(400); + const resultBody = JSON.parse(result.body); + expect(resultBody.message).to.eq("size must be >= 1"); + }); + }); + + it("paginates results using default size and page number", async () => { + mock + .post( + "/dc-v2-work/_search", + makeQuery({ + size: 5, + from: 0, + query: { + more_like_this: { + fields: [ + "title", + "description", + "subject.label", + "genre.label", + "contributor.label", + "creator.label", + ], + like: [ + { + _index: "dc-v2-work", + _id: 1234, + }, + ], + max_query_terms: 10, + min_doc_freq: 1, + min_term_freq: 1, + }, + }, + }) + ) + .reply(200, helpers.testFixture("mocks/similar.json")); + const event = baseEvent.render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + const { + pagination: { query_url }, + } = JSON.parse(result.body); + const url = new URL(query_url); + expect(url.searchParams.has("searchToken")).to.be.false; + expect(url.searchParams.has("size")).to.be.false; + }); + + it("paginates results using provided size and page number", async () => { + let mocked = mock + .post( + "/dc-v2-work/_search", + makeQuery({ + size: 3, + from: 6, + query: { + more_like_this: { + fields: [ + "title", + "description", + "subject.label", + "genre.label", + "contributor.label", + "creator.label", + ], + like: [ + { + _index: "dc-v2-work", + _id: 1234, + }, + ], + max_query_terms: 10, + min_doc_freq: 1, + min_term_freq: 1, + }, + }, + }) + ) + .reply(200, helpers.testFixture("mocks/similar.json")); + const event = baseEvent.queryParams({ page: 3, size: 3 }).render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result.headers).to.include({ "content-type": "application/json" }); + const resultBody = JSON.parse(result.body); + expect(resultBody.pagination.current_page).to.eq(3); + expect(resultBody.pagination.limit).to.eq(3); + expect(resultBody.pagination.offset).to.eq(6); + expect(resultBody.pagination.total_hits).to.eq(9); + expect(resultBody.pagination.total_pages).to.eq(3); + const url = new URL(resultBody.pagination.query_url); + expect(url.searchParams.has("searchToken")).to.be.false; + expect(url.searchParams.get("size")).to.eq("3"); + }); +}); From cae00600a2c5624e5b73644e6863d26508e3a32e Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 17 Oct 2022 17:21:52 +0000 Subject: [PATCH 34/61] Refactor search handler to use more flexible doSearch --- src/api/pagination.js | 10 +- src/api/request/pipeline.js | 3 +- src/api/response/iiif/collection.js | 38 ++-- src/handlers/get-collection-by-id.js | 39 +++-- src/handlers/get-collections.js | 52 +----- src/handlers/get-similar.js | 53 +----- src/handlers/search-runner.js | 154 ++++++++++++++++ src/handlers/search.js | 164 +----------------- ...-by-id.js => get-collection-by-id.test.js} | 0 test/integration/get-collections.test.js | 22 +-- test/integration/get-similar.test.js | 24 +-- test/integration/search.test.js | 2 +- test/unit/api/pagination.test.js | 2 +- test/unit/api/request/pipeline.test.js | 15 +- .../unit/api/response/iiif/collection.test.js | 9 +- 15 files changed, 247 insertions(+), 340 deletions(-) create mode 100644 src/handlers/search-runner.js rename test/integration/{get-collection-by-id.js => get-collection-by-id.test.js} (100%) diff --git a/src/api/pagination.js b/src/api/pagination.js index d3710742..41586869 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -69,15 +69,17 @@ class Paginator { ); } - const extraParams = this?.options?.extraParams; - if (typeof extraParams === "object") { - for (const param in extraParams) { - url.searchParams.set(param, extraParams[param]); + const queryStringParameters = + this.options?.parameterOverrides || this.options?.queryStringParameters; + if (typeof queryStringParameters === "object") { + for (const param in queryStringParameters) { + url.searchParams.set(param, queryStringParameters[param]); } } const prev = prevPage(this.body, count); const next = nextPage(this.body, count); + url.searchParams.delete("from"); let result = { query_url: url.toString(), diff --git a/src/api/request/pipeline.js b/src/api/request/pipeline.js index 06c1f6a8..73525eb2 100644 --- a/src/api/request/pipeline.js +++ b/src/api/request/pipeline.js @@ -15,7 +15,8 @@ function filterFor(query, event) { module.exports = class RequestPipeline { constructor(searchContext) { this.searchContext = { ...searchContext }; - this.searchContext.query ||= { match_all: {} }; + if (!this.searchContext.size) this.searchContext.size = 10; + if (!this.searchContext.from) this.searchContext.from = 0; } // Things tranformer needs to do: diff --git a/src/api/response/iiif/collection.js b/src/api/response/iiif/collection.js index 59c6ab7e..32c4425a 100644 --- a/src/api/response/iiif/collection.js +++ b/src/api/response/iiif/collection.js @@ -17,26 +17,28 @@ async function transform(response, pager) { } async function buildCollection(responseBody, pageInfo) { - const collectionLabel = pageInfo.options.collectionLabel; - const collectionSummary = pageInfo.options.collectionSummary; + const { + options: { + queryStringParameters: { + collectionLabel = "IIIF Collection", + collectionSummary = "", + }, + }, + } = pageInfo; return { "@context": ["http://iiif.io/api/presentation/3/context.json"], id: collectionId(pageInfo), type: "Collection", - label: { - none: [`${collectionLabel}`], - }, - summary: { - none: [`${collectionSummary}`], - }, + label: { none: [collectionLabel] }, + summary: { none: [collectionSummary] }, homepage: [ { id: homepageUrl(pageInfo), type: "Text", format: "text/html", label: { - none: [`Results for ${collectionLabel} of ${collectionSummary}`], + none: [collectionLabel], }, }, ], @@ -69,8 +71,22 @@ function collectionId(pageInfo) { } function homepageUrl(pageInfo) { - const result = new URL("/search", dcUrl); - result.searchParams.set("q", pageInfo.options.queryString); + let result; + + if (pageInfo.options?.parameterOverrides) { + const collectionId = new URL(pageInfo.query_url).pathname + .split("/") + .reverse()[0]; + result = new URL(`/collections/${collectionId}`, dcUrl); + } else { + result = new URL("/search", dcUrl); + if (pageInfo.options?.queryStringParameters?.query) + result.searchParams.set( + "q", + pageInfo.options.queryStringParameters.query + ); + } + return result; } diff --git a/src/handlers/get-collection-by-id.js b/src/handlers/get-collection-by-id.js index 1e55b368..f4d1f9fd 100644 --- a/src/handlers/get-collection-by-id.js +++ b/src/handlers/get-collection-by-id.js @@ -1,26 +1,37 @@ const { processRequest, processResponse } = require("./middleware"); const { getCollection } = require("../api/opensearch"); -const { getSearch } = require("./search"); +const { doSearch } = require("./search-runner"); const opensearchResponse = require("../api/response/opensearch"); -/** - * Get a colletion by id - */ -exports.handler = async (event) => { +const getCollectionById = async (event) => { event = processRequest(event); const id = event.pathParameters.id; const esResponse = await getCollection(id); + const response = opensearchResponse.transform(esResponse); + return processResponse(event, response); +}; +const getIiifCollectionById = async (event) => { + const id = event.pathParameters.id; + const esResponse = await getCollection(id); const collection = JSON.parse(esResponse.body)?._source; + if (!collection) return { statusCode: 404, body: "Not Found" }; + const parameterOverrides = { ...event.queryStringParameters }; - if (event.queryStringParameters?.as === "iiif" && collection) { - event.queryStringParameters.query = `collection.id:${id}`; - event.queryStringParameters.collectionLabel = collection?.title || ""; - event.queryStringParameters.collectionSummary = - collection?.description || ""; - return await getSearch(event); - } + event.queryStringParameters.query = `collection.id:${id}`; + event.queryStringParameters.collectionLabel = collection?.title; + event.queryStringParameters.collectionSummary = collection?.description; + return await doSearch(event, { + includeToken: false, + parameterOverrides, + }); +}; - const response = opensearchResponse.transform(esResponse); - return processResponse(event, response); +/** + * Get a colletion by id + */ +exports.handler = async (event) => { + return event.queryStringParameters?.as === "iiif" + ? getIiifCollectionById(event) + : getCollectionById(event); }; diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index 51a1971d..89e7dbc8 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -1,54 +1,10 @@ -const { processRequest, processResponse } = require("./middleware"); -const { baseUrl } = require("../helpers"); -const { modelsToTargets } = require("../api/request/models"); -const { search } = require("../api/opensearch"); -const opensearchResponse = require("../api/response/opensearch"); -const { Paginator } = require("../api/pagination"); -const RequestPipeline = require("../api/request/pipeline"); - -const DefaultSize = 10; - -const numberParam = (value, defaultValue) => { - const result = Number(value); - if (isNaN(result)) return defaultValue; - return result; -}; +const { doSearch } = require("./search-runner"); /** * A simple function to get Collections */ exports.handler = async (event) => { - event = processRequest(event); - - const page = numberParam(event.queryStringParameters?.page, 1); - if (page < 1) return invalidRequest("page must be >= 1"); - const size = numberParam(event.queryStringParameters?.size, DefaultSize); - if (size < 1) return invalidRequest("size must be >= 1"); - - let body = { size: size, from: size * (page - 1) }; - let models = ["collections"]; - - const extraParams = size === DefaultSize ? {} : { size }; - const pager = new Paginator( - baseUrl(event), - "collections", - models, - body, - null, - { extraParams, includeToken: false } - ); - const filteredBody = new RequestPipeline(body).authFilter(event).toJson(); - let esResponse = await search(modelsToTargets(models), filteredBody); - let transformedResponse = await opensearchResponse.transform( - esResponse, - pager - ); - return processResponse(event, transformedResponse); -}; - -const invalidRequest = (message) => { - return { - statusCode: 400, - body: JSON.stringify({ message: message }), - }; + event.pathParameters.models = "collections"; + event.body = { query: { match_all: {} } }; + return doSearch(event, { includeToken: false }); }; diff --git a/src/handlers/get-similar.js b/src/handlers/get-similar.js index 12bcf295..f4517d4f 100644 --- a/src/handlers/get-similar.js +++ b/src/handlers/get-similar.js @@ -1,37 +1,15 @@ -const { baseUrl } = require("../helpers"); +const { doSearch } = require("./search-runner"); const { modelsToTargets } = require("../api/request/models"); -const { search } = require("../api/opensearch"); -const opensearchResponse = require("../api/response/opensearch"); -const { Paginator } = require("../api/pagination"); -const { processRequest, processResponse } = require("./middleware"); -const RequestPipeline = require("../api/request/pipeline"); - -const DefaultSize = 5; - -const numberParam = (value, defaultValue) => { - const result = Number(value); - if (isNaN(result)) return defaultValue; - return result; -}; /** * Get similar works via 'More Like This' query */ exports.handler = async (event) => { - event = processRequest(event); - const id = event.pathParameters.id; - const page = numberParam(event.queryStringParameters?.page, 1); - if (page < 1) return invalidRequest("page must be >= 1"); - const size = numberParam(event.queryStringParameters?.size, DefaultSize); - if (size < 1) return invalidRequest("size must be >= 1"); - const models = ["works"]; const workIndex = modelsToTargets(models); - let body = { - size: size, - from: size * (page - 1), + event.body = { query: { more_like_this: { fields: [ @@ -55,30 +33,5 @@ exports.handler = async (event) => { }, }; - const extraParams = size === DefaultSize ? {} : { size }; - const pager = new Paginator( - baseUrl(event), - `works/${id}/similar`, - models, - body, - null, - { - extraParams, - includeToken: false, - } - ); - const filteredBody = new RequestPipeline(body).authFilter().toJson(); - let esResponse = await search(workIndex, filteredBody); - let transformedResponse = await opensearchResponse.transform( - esResponse, - pager - ); - return processResponse(event, transformedResponse); -}; - -const invalidRequest = (message) => { - return { - statusCode: 400, - body: JSON.stringify({ message: message }), - }; + return doSearch(event, { includeToken: false }); }; diff --git a/src/handlers/search-runner.js b/src/handlers/search-runner.js new file mode 100644 index 00000000..ff4b675f --- /dev/null +++ b/src/handlers/search-runner.js @@ -0,0 +1,154 @@ +const { baseUrl } = require("../helpers"); +const { processRequest, processResponse } = require("./middleware"); +const { + extractRequestedModels, + modelsToTargets, + validModels, +} = require("../api/request/models"); +const { search } = require("../api/opensearch"); +const responseTransformer = require("../api/response/transformer"); +const { decodeSearchToken, Paginator } = require("../api/pagination"); +const RequestPipeline = require("../api/request/pipeline"); +const Path = require("path"); + +/** + * Function to wrap search requests and transform responses + */ +const doSearch = async (event, searchParams = {}) => { + event = processRequest(event); + const models = extractRequestedModels(event.pathParameters?.models); + const format = await responseFormat(event); + + let searchContext; + try { + searchContext = await constructSearchContext(event); + } catch (error) { + return invalidRequest(error.message); + } + + if (!validModels(models, format)) { + return invalidRequest(`Invalid models requested: ${models}`); + } + + const base = new URL(baseUrl(event)); + const path = Path.relative(base.pathname, event.requestContext?.http?.path); + + const pager = new Paginator( + base.toString(), + path, + models, + searchContext, + format, + { + ...searchParams, + queryStringParameters: event.queryStringParameters, + } + ); + const filteredSearchContext = new RequestPipeline(searchContext) + .authFilter(event) + .toJson(); + + const esResponse = await search( + modelsToTargets(models), + filteredSearchContext + ); + + const response = await responseTransformer.transformSearchResult( + esResponse, + pager + ); + return processResponse(event, response); +}; + +const invalidRequest = (message) => { + return { + statusCode: 400, + body: JSON.stringify({ message: message }), + }; +}; + +const constructSearchContext = async (event) => { + let searchContext; + + if (typeof event.body === "object") { + searchContext = event.body; + } else if (event.requestContext.http.method === "POST") { + searchContext = JSON.parse(event.body); + } else { + const token = event.queryStringParameters?.searchToken; + searchContext = + token === undefined || token === "" + ? fromQueryString(event) + : await fromToken(event); + } + + const { queryStringParameters = {} } = event; + + searchContext.size = queryStringParameters.size || searchContext.size || 10; + searchContext.from = queryStringParameters.from || searchContext.from || 0; + + if (queryStringParameters.page) { + const page = Number(queryStringParameters.page || 1); + const size = Number(queryStringParameters.size || 10); + searchContext.from = (page - 1) * size; + } + + // if (queryStringParameters.sort) { + // //TODO + // } + + return searchContext; +}; + +const fromQueryString = ({ queryStringParameters, requestContext }) => { + if (queryStringParameters?.query) { + return { + query: { + query_string: { + query: queryStringParameters.query, + }, + }, + }; + } else { + return { + query: { + query_string: { + query: "*", + }, + }, + }; + } +}; + +const responseFormat = async (event) => { + if (event.queryStringParameters?.as) { + return event.queryStringParameters?.as; + } else { + if (event.queryStringParameters?.searchToken === undefined) + return "default"; + const token = event.queryStringParameters.searchToken; + + try { + request = await decodeSearchToken(token); + } catch (err) { + return invalidRequest("searchToken is invalid"); + } + return request.format; + } +}; + +const fromToken = async (event) => { + const token = event.queryStringParameters.searchToken; + + let request; + + try { + request = await decodeSearchToken(token); + } catch (err) { + throw new Error("searchToken is invalid"); + } + + return request.body; +}; + +module.exports = { doSearch }; diff --git a/src/handlers/search.js b/src/handlers/search.js index df29def9..9ff38d5d 100644 --- a/src/handlers/search.js +++ b/src/handlers/search.js @@ -1,166 +1,10 @@ -const { processRequest, processResponse } = require("./middleware"); -const { baseUrl } = require("../helpers"); -const { - extractRequestedModels, - modelsToTargets, - validModels, -} = require("../api/request/models"); -const { search } = require("../api/opensearch"); -const responseTransformer = require("../api/response/transformer"); -const { decodeSearchToken, Paginator } = require("../api/pagination"); -const RequestPipeline = require("../api/request/pipeline"); +const { doSearch } = require("./search-runner"); const getSearch = async (event) => { - event = processRequest(event); - const models = extractRequestedModels(event.pathParameters?.models); - const format = await responseFormat(event); - - let searchContext; - - try { - searchContext = await constructSearchContext(event); - } catch (error) { - return invalidRequest(error.message); - } - - const response = await executeSearch( - event, - models, - searchContext, - format, - getOptions(event.queryStringParameters, format) - ); - return processResponse(event, response); -}; - -const postSearch = async (event) => { - event = processRequest(event); - - const searchContext = JSON.parse(event.body); - const models = extractRequestedModels(event.pathParameters?.models); - - const response = await executeSearch(event, models, searchContext); - return processResponse(event, response); -}; - -/** - * Function to wrap search requests and transform responses - */ -const executeSearch = async ( - event, - models, - searchContext, - format = "default", - options = {} -) => { - if (!validModels(models, format)) { - return invalidRequest(`Invalid models requested: ${models}`); - } - - const pager = new Paginator( - baseUrl(event), - "search", - models, - searchContext, - format, - options - ); - const filteredSearchContext = new RequestPipeline(searchContext) - .authFilter(event) - .toJson(); - const esResponse = await search( - modelsToTargets(models), - filteredSearchContext - ); - - return await responseTransformer.transformSearchResult(esResponse, pager); -}; - -const invalidRequest = (message) => { - return { - statusCode: 400, - body: JSON.stringify({ message: message }), - }; -}; - -const constructSearchContext = async (event) => { - const token = event.queryStringParameters?.searchToken; - - return token === undefined || token === "" - ? fromQueryString(event.queryStringParameters) - : await fromBody(event); + const includeToken = !!event.queryStringParameters?.searchToken; + return await doSearch(event, { includeToken }); }; -const fromQueryString = (queryStringParameters) => { - let searchContext = { - query: { - query_string: { - query: queryStringParameters?.query || "*", - }, - }, - }; - - if (queryStringParameters?.size) { - searchContext.size = queryStringParameters.size; - } - - if (queryStringParameters?.from) { - searchContext.from = queryStringParameters.from; - } - - if (queryStringParameters?.sort) { - //TODO - } - return searchContext; -}; - -const responseFormat = async (event) => { - if (event.queryStringParameters?.as) { - return event.queryStringParameters?.as; - } else { - if (event.queryStringParameters?.searchToken === undefined) - return "default"; - const token = event.queryStringParameters.searchToken; - - try { - request = await decodeSearchToken(token); - } catch (err) { - return invalidRequest("searchToken is invalid"); - } - return request.format; - } -}; - -const fromBody = async (event) => { - const token = event.queryStringParameters.searchToken; - - let request; - - try { - request = await decodeSearchToken(token); - } catch (err) { - throw new Error("searchToken is invalid"); - } - - const page = Number(event.queryStringParameters.page || 1); - request.body.from = request.body.size * (page - 1); - - return request.body; -}; - -const getOptions = (queryStringParameters, format) => { - if (format === "iiif") { - const collectionLabel = - queryStringParameters?.collectionLabel || "IIIF Collection"; - const collectionSummary = queryStringParameters?.collectionSummary || ""; - const queryString = queryStringParameters.query || "*"; - return { - collectionLabel: collectionLabel, - collectionSummary: collectionSummary, - queryString: queryString, - }; - } - return {}; -}; +const postSearch = async (event) => await doSearch(event); module.exports = { postSearch, getSearch }; diff --git a/test/integration/get-collection-by-id.js b/test/integration/get-collection-by-id.test.js similarity index 100% rename from test/integration/get-collection-by-id.js rename to test/integration/get-collection-by-id.test.js diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 46ff442f..7267cf0e 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -16,25 +16,9 @@ describe("Collections route", () => { .pathPrefix("/api/v2"); let event; const makeQuery = (params) => - new RequestPipeline(params).authFilter().toJson(); - - describe("validates parameters", () => { - it("page", async () => { - event = baseEvent.queryParams({ page: 0 }).render(); - const result = await handler(event); - expect(result.statusCode).to.eq(400); - const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("page must be >= 1"); - }); - - it("size", async () => { - event = baseEvent.queryParams({ size: 0 }).render(); - const result = await handler(event); - expect(result.statusCode).to.eq(400); - const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("size must be >= 1"); - }); - }); + new RequestPipeline({ query: { match_all: {} }, ...params }) + .authFilter() + .toJson(); it("paginates results using default size and page number", async () => { mock diff --git a/test/integration/get-similar.test.js b/test/integration/get-similar.test.js index 89d672be..1c530349 100644 --- a/test/integration/get-similar.test.js +++ b/test/integration/get-similar.test.js @@ -15,31 +15,11 @@ describe("Similar routes", () => { const makeQuery = (params) => new RequestPipeline(params).authFilter().toJson(); - describe("validates parameters", () => { - it("page", async () => { - const event = baseEvent.queryParams({ page: 0 }).render(); - const result = await handler(event); - expect(result.statusCode).to.eq(400); - const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("page must be >= 1"); - }); - - it("size", async () => { - const event = baseEvent.queryParams({ size: 0 }).render(); - const result = await handler(event); - expect(result.statusCode).to.eq(400); - const resultBody = JSON.parse(result.body); - expect(resultBody.message).to.eq("size must be >= 1"); - }); - }); - it("paginates results using default size and page number", async () => { mock .post( "/dc-v2-work/_search", makeQuery({ - size: 5, - from: 0, query: { more_like_this: { fields: [ @@ -81,8 +61,6 @@ describe("Similar routes", () => { .post( "/dc-v2-work/_search", makeQuery({ - size: 3, - from: 6, query: { more_like_this: { fields: [ @@ -104,6 +82,8 @@ describe("Similar routes", () => { min_term_freq: 1, }, }, + size: 3, + from: 6, }) ) .reply(200, helpers.testFixture("mocks/similar.json")); diff --git a/test/integration/search.test.js b/test/integration/search.test.js index 0b29cc79..5e9abd37 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -72,7 +72,7 @@ describe("Search routes", () => { describe("GET /search", () => { const handler = searchHandlers.getSearch; - const originalQuery = { query: { match_all: {} }, size: 10, from: 0 }; + const originalQuery = { query: { match_all: {} } }; const authQuery = new RequestPipeline(originalQuery).authFilter().toJson(); const searchToken = "N4IgRg9gJgniBcoCOBXApgJzokBbAhgC4DGAFgPr4A2VCwAvvQDQgDOAlgF5oICMADMzzQ0VVggDaIAO4QMAa3EBdekA"; diff --git a/test/unit/api/pagination.test.js b/test/unit/api/pagination.test.js index 218d5e4c..15e2e696 100644 --- a/test/unit/api/pagination.test.js +++ b/test/unit/api/pagination.test.js @@ -81,7 +81,7 @@ describe("Paginator", function () { }); it("includes extra parameters", async () => { - pager.options = { extraParams: { size: 5 } }; + pager.options = { queryStringParameters: { size: 5 } }; const result = await pager.pageInfo(1275); const url = new URL(result.query_url); expect(url.searchParams.get("size")).to.eq("5"); diff --git a/test/unit/api/request/pipeline.test.js b/test/unit/api/request/pipeline.test.js index 5f023df7..fadfe91d 100644 --- a/test/unit/api/request/pipeline.test.js +++ b/test/unit/api/request/pipeline.test.js @@ -12,6 +12,7 @@ describe("RequestPipeline", () => { const requestBody = { query: { match: { term: { title: "The Title" } } }, size: 50, + from: 0, sort: [{ create_date: "asc" }], _source: ["id", "title", "collection"], aggs: { collection: { terms: { field: "contributor.label", size: 10 } } }, @@ -22,12 +23,14 @@ describe("RequestPipeline", () => { pipeline = new RequestPipeline(requestBody); }); - it("sets a default query", () => { - const result = new RequestPipeline({ size: 20 }).authFilter(event); - expect(result.searchContext.size).to.eq(20); - expect(result.searchContext.query.bool.must).to.deep.include({ - match_all: {}, - }); + it("adds an auth filter", () => { + const result = pipeline.authFilter(event); + expect(result.searchContext.size).to.eq(50); + expect(result.searchContext.query.bool.must).to.include(requestBody.query); + expect(result.searchContext.query.bool.must_not).to.deep.include( + { term: { visibility: "Private" } }, + { term: { published: false } } + ); }); it("serializes JSON", () => { diff --git a/test/unit/api/response/iiif/collection.test.js b/test/unit/api/response/iiif/collection.test.js index 156ddd47..1a398ff7 100644 --- a/test/unit/api/response/iiif/collection.test.js +++ b/test/unit/api/response/iiif/collection.test.js @@ -15,9 +15,12 @@ describe("IIIF Collection response transformer", () => { { query: { query_string: { query: "genre.label:architecture" } } }, "iiif", { - collectionLabel: "The collection label", - collectionSummary: "The collection Summary", - queryString: "genre.label:architecture", + includeToken: false, + queryStringParameters: { + collectionLabel: "The collection label", + collectionSummary: "The collection Summary", + query: "genre.label:architecture", + }, } ); }); From 3e768091774f912b70713df12def42f1f5ab11ae Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 19 Oct 2022 21:49:43 +0000 Subject: [PATCH 35/61] Allow GET /search to include a models path param --- template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.yaml b/template.yaml index 93e4d511..a5f53592 100644 --- a/template.yaml +++ b/template.yaml @@ -298,7 +298,7 @@ Resources: Type: HttpApi Properties: ApiId: !Ref dcApi - Path: /search + Path: /search/{models} Method: GET optionsFunction: Type: AWS::Serverless::Function From b61e18faa47c0f7079e6af467dfe2918d9fa199c Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 15:30:42 +0000 Subject: [PATCH 36/61] Fix template & add template validation to CI build --- .github/workflows/build.yml | 14 +++++++++++--- template.yaml | 10 ---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da7e09bc..9bc1142f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,11 +3,17 @@ on: - push jobs: test: - env: - AWS_ACCESS_KEY_ID: ci - AWS_SECRET_ACCESS_KEY: ci runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: staging steps: + - uses: aws-actions/setup-sam@v1 + - uses: aws-actions/configure-aws-credentials@master + with: + role-to-assume: arn:aws:iam::${{ secrets.AwsAccount }}:role/github-actions-role + aws-region: us-east-1 - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: @@ -18,3 +24,5 @@ jobs: run: npm run prettier - name: Run tests run: npm run test:coverage + - name: Validate template + run: sam build && sam validate diff --git a/template.yaml b/template.yaml index a5f53592..b4874343 100644 --- a/template.yaml +++ b/template.yaml @@ -226,12 +226,7 @@ Resources: getSimilarFunction: Type: AWS::Serverless::Function Properties: - CodeUri: ./src Handler: handlers/get-similar.handler - Runtime: nodejs16.x - Architectures: - - x86_64 - MemorySize: 128 Timeout: 100 Description: Gets works similar to a specific work. Policies: @@ -242,11 +237,6 @@ Resources: Action: - es:ESHttp* Resource: "*" - Environment: - Variables: - API_TOKEN_SECRET: !Ref ApiSecret - ENV_PREFIX: !Ref EnvironmentPrefix - ELASTICSEARCH_ENDPOINT: !Ref ElasticsearchEndpoint Events: WorkApi: Type: HttpApi From 21d27864c382f52f04c4f7637075b4b02f747b9c Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 17:39:59 +0000 Subject: [PATCH 37/61] Ensure event object has expected members Update swagger docs to include correct query parameters Add swagger entry for /works/{id}/similar --- docs/docs/spec/openapi.yaml | 105 ++++++++++------------------- docs/docs/spec/types.yaml | 59 ++++++++++++++++ src/handlers/middleware.js | 4 +- src/helpers.js | 10 +++ test/test-helpers/event-builder.js | 3 - test/unit/api/helpers.test.js | 10 +++ 6 files changed, 118 insertions(+), 73 deletions(-) diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index 51a5d591..8aaf2c8f 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -26,13 +26,8 @@ paths: tags: - Collection parameters: - - name: page - in: query - required: false - description: Page number of results to retrieve - schema: - type: integer - minimum: 1 + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" @@ -43,6 +38,7 @@ paths: operationId: getCollectionById parameters: - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/as" responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" @@ -53,23 +49,9 @@ paths: - Collection parameters: - $ref: "./types.yaml#/components/parameters/id" - - name: aspect - in: query - required: false - description: Desired aspect ratio - schema: - type: string - enum: - - full - - square - - name: size - in: query - required: false - description: Size of largest dimension - schema: - type: integer - minimum: 1 - maximum: 300 + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/thumbnailAspect" + - $ref: "./types.yaml#/components/parameters/thumbnailSize" responses: 200: description: A thumbnail image for the given collection @@ -98,6 +80,16 @@ paths: responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" + /works/{id}/similar: + get: + operationId: getSimilarWorks + tags: + - Work + parameters: + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/as" /works/{id}/thumbnail: get: operationId: getWorkThumbnail @@ -105,23 +97,8 @@ paths: - Work parameters: - $ref: "./types.yaml#/components/parameters/id" - - name: aspect - in: query - required: false - description: Desired aspect ratio - schema: - type: string - enum: - - full - - square - - name: size - in: query - required: false - description: Size of largest dimension - schema: - type: integer - minimum: 1 - maximum: 300 + - $ref: "./types.yaml#/components/parameters/thumbnailAspect" + - $ref: "./types.yaml#/components/parameters/thumbnailSize" responses: 200: description: A thumbnail image for the given work @@ -136,34 +113,11 @@ paths: tags: - Search parameters: - - name: query - in: query - required: false - description: Keyword query - schema: - type: string - - name: searchToken - in: query - required: false - description: Search token from previous search response - schema: - type: string - - name: page - in: query - required: false - description: Page number of results to retrieve - schema: - type: integer - minimum: 1 - - name: as - in: query - required: false - description: Desired output format - schema: - type: string - enum: - - iiif - - opensearch + - $ref: "./types.yaml#/components/parameters/query" + - $ref: "./types.yaml#/components/parameters/searchToken" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/as" responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" @@ -181,6 +135,19 @@ paths: 200: $ref: "./types.yaml#/components/responses/SearchResponse" /search/{models}: + get: + operationId: getSearch + tags: + - Search + parameters: + - $ref: "./types.yaml#/components/parameters/query" + - $ref: "./types.yaml#/components/parameters/searchToken" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/as" + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" post: operationId: postSearchWithModels tags: diff --git a/docs/docs/spec/types.yaml b/docs/docs/spec/types.yaml index a14865fc..5002f3f1 100644 --- a/docs/docs/spec/types.yaml +++ b/docs/docs/spec/types.yaml @@ -9,6 +9,65 @@ components: schema: type: string format: uuid + query: + name: query + in: query + required: false + description: Keyword query + schema: + type: string + searchToken: + name: searchToken + in: query + required: false + description: Search token from previous search response + schema: + type: string + page: + name: page + in: query + required: false + description: Page number of results to retrieve + schema: + type: integer + minimum: 1 + size: + name: size + in: query + required: false + description: Maximum number of results per page + schema: + type: integer + minimum: 0 + as: + name: as + in: query + required: false + description: Desired output format + schema: + type: string + enum: + - iiif + - opensearch + thumbnailAspect: + name: aspect + in: query + required: false + description: Desired aspect ratio + schema: + type: string + enum: + - full + - square + thumbnailSize: + name: size + in: query + required: false + description: Size of largest dimension + schema: + type: integer + minimum: 1 + maximum: 300 responses: DocumentResponse: description: A single document response diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js index 4fbc881e..4bedd279 100644 --- a/src/handlers/middleware.js +++ b/src/handlers/middleware.js @@ -3,10 +3,12 @@ const { decodeEventBody, normalizeHeaders, objectifyCookies, + stubEventMembers, } = require("../helpers"); const processRequest = function (event) { - let result = normalizeHeaders(event); + let result = stubEventMembers(event); + result = normalizeHeaders(event); result = objectifyCookies(result); result = decodeEventBody(result); return result; diff --git a/src/helpers.js b/src/helpers.js index 1d4035b5..316ff2d5 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -102,6 +102,15 @@ function isFromReadingRoom(event) { return AllowedIPs.includes(sourceIp); } +function stubEventMembers(event) { + event.headers ||= {}; + event.pathParameters ||= {}; + event.queryStringParameters ||= {}; + event.stageVariables ||= {}; + event.cookies ||= []; + return event; +} + module.exports = { addCorsHeaders, baseUrl, @@ -109,4 +118,5 @@ module.exports = { isFromReadingRoom, normalizeHeaders, objectifyCookies, + stubEventMembers, }; diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index 76942b7e..f814f8a9 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -15,7 +15,6 @@ module.exports = class { "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https", }, - queryStringParameters: {}, requestContext: { accountId: "123456789012", apiId: "pewpew", @@ -35,9 +34,7 @@ module.exports = class { timeEpoch: Number(now), }, body: "", - pathParameters: {}, isBase64Encoded: false, - stageVariables: {}, }; } diff --git a/test/unit/api/helpers.test.js b/test/unit/api/helpers.test.js index 11d04bef..986c75d7 100644 --- a/test/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -6,6 +6,7 @@ const { isFromReadingRoom, normalizeHeaders, objectifyCookies, + stubEventMembers, } = require("../../../src/helpers"); const chai = require("chai"); const expect = chai.expect; @@ -200,4 +201,13 @@ describe("helpers", () => { }); }); }); + + describe("stubEventMembers", () => { + it("makes sure the event has all expected members", () => { + const result = stubEventMembers({}); + expect(result.cookies).to.eql([]); + expect(result.pathParameters).to.eql({}); + expect(result.queryStringParameters).to.eql({}); + }); + }); }); From 9955cfd52cbc13cd8a49b2ed542c6eff2f479ba6 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 19:09:18 +0000 Subject: [PATCH 38/61] Fix 500 error in GET /collections Leave optional members undefined in test event: queryStringParameters, pathParameters, stageVariables, and cookies Make middleware.processRequest idempotent --- src/handlers/get-collections.js | 2 ++ src/handlers/middleware.js | 2 ++ test/test-helpers/event-builder.js | 27 ++++++++++++++++----------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/handlers/get-collections.js b/src/handlers/get-collections.js index 89e7dbc8..8ede301a 100644 --- a/src/handlers/get-collections.js +++ b/src/handlers/get-collections.js @@ -1,9 +1,11 @@ const { doSearch } = require("./search-runner"); +const { processRequest } = require("./middleware"); /** * A simple function to get Collections */ exports.handler = async (event) => { + event = processRequest(event); event.pathParameters.models = "collections"; event.body = { query: { match_all: {} } }; return doSearch(event, { includeToken: false }); diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js index 4bedd279..4daf456c 100644 --- a/src/handlers/middleware.js +++ b/src/handlers/middleware.js @@ -7,10 +7,12 @@ const { } = require("../helpers"); const processRequest = function (event) { + if (event._processRequest) return event; let result = stubEventMembers(event); result = normalizeHeaders(event); result = objectifyCookies(result); result = decodeEventBody(result); + result._processRequest = true; return result; }; diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index f814f8a9..db590d8d 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -102,20 +102,25 @@ module.exports = class { result.cookies = cookies.split(/;\s*/); } - result.pathParameters = { ...this._pathParams }; result.rawPath = this._pathPrefix + this._route; - for (const param in result.pathParameters) { - result.rawPath = result.rawPath.replace( - `{${param}}`, - result.pathParameters[param] - ); - } result.requestContext.http.path = result.rawPath; - result.queryStringParameters = { ...this._queryParams }; - result.rawQueryString = new URLSearchParams( - result.queryStringParameters - ).toString(); + if (this._pathParams) { + result.pathParameters = { ...this._pathParams }; + for (const param in result.pathParameters) { + result.rawPath = result.rawPath.replace( + `{${param}}`, + result.pathParameters[param] + ); + } + } + + if (this._queryParams) { + result.queryStringParameters = { ...this._queryParams }; + result.rawQueryString = new URLSearchParams( + result.queryStringParameters + ).toString(); + } return result; } From 4556070fdc1194f5e0d272393b14efb9655c2559 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 19:35:53 +0000 Subject: [PATCH 39/61] Fix errors in OpenAPI spec document Add OpenAPI validation test Split sam validation test into parallel job --- .github/workflows/build.yml | 35 ++++++++++++++++++++++++++--------- docs/docs/spec/openapi.yaml | 5 ++++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bc1142f..a2ada843 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,16 +4,10 @@ on: jobs: test: runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - environment: staging + env: + AWS_ACCESS_KEY_ID: ci + AWS_SECRET_ACCESS_KEY: ci steps: - - uses: aws-actions/setup-sam@v1 - - uses: aws-actions/configure-aws-credentials@master - with: - role-to-assume: arn:aws:iam::${{ secrets.AwsAccount }}:role/github-actions-role - aws-region: us-east-1 - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: @@ -24,5 +18,28 @@ jobs: run: npm run prettier - name: Run tests run: npm run test:coverage + validate-template: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + environment: staging + steps: + - uses: aws-actions/setup-sam@v1 + - uses: aws-actions/configure-aws-credentials@master + with: + role-to-assume: arn:aws:iam::${{ secrets.AwsAccount }}:role/github-actions-role + aws-region: us-east-1 + - uses: actions/checkout@v3 - name: Validate template run: sam build && sam validate + validate-api-spec: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Validate OpenAPI Spec + run: | + docker run -u 1001 --rm --workdir /github/workspace \ + -v $GITHUB_WORKSPACE:/github/workspace \ + openapitools/openapi-generator-cli:latest validate \ + -i /github/workspace/docs/docs/spec/openapi.yaml diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index 8aaf2c8f..5fcd13ef 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -90,6 +90,9 @@ paths: - $ref: "./types.yaml#/components/parameters/page" - $ref: "./types.yaml#/components/parameters/size" - $ref: "./types.yaml#/components/parameters/as" + responses: + 200: + $ref: "./types.yaml#/components/responses/SearchResponse" /works/{id}/thumbnail: get: operationId: getWorkThumbnail @@ -136,7 +139,7 @@ paths: $ref: "./types.yaml#/components/responses/SearchResponse" /search/{models}: get: - operationId: getSearch + operationId: getSearchWithModels tags: - Search parameters: From 12479899286560d9381b2ff28690b8937e846456 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 20:48:54 +0000 Subject: [PATCH 40/61] Add default charset if missing from content-type header await all calls to opensearchResponse.transform --- .github/workflows/build.yml | 2 +- package-lock.json | 599 +++++++++++++++++- package.json | 1 + src/handlers/get-collection-by-id.js | 2 +- src/handlers/get-file-set-by-id.js | 2 +- src/handlers/get-shared-link-by-id.js | 2 +- src/handlers/get-work-by-id.js | 2 +- src/handlers/middleware.js | 5 +- src/helpers.js | 14 + src/package-lock.json | 13 +- src/package.json | 3 +- test/integration/get-collection-by-id.test.js | 11 +- test/integration/get-collections.test.js | 11 +- test/integration/get-doc.test.js | 16 +- .../integration/get-shared-link-by-id.test.js | 6 +- test/integration/get-similar.test.js | 11 +- test/integration/search.test.js | 31 +- 17 files changed, 705 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a2ada843..5328925e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: permissions: id-token: write contents: read - environment: staging + environment: test steps: - uses: aws-actions/setup-sam@v1 - uses: aws-actions/configure-aws-credentials@master diff --git a/package-lock.json b/package-lock.json index 063cc087..0ebb3c94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ }, "devDependencies": { "chai": "^4.2.0", + "chai-http": "^4.3.0", "mocha": "^9.1.4", "nock": "^13.2.9", "nyc": "^15.1.0", @@ -1215,6 +1216,34 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@types/chai": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "dev": true + }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.11.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", + "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==", + "dev": true + }, + "node_modules/@types/superagent": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", + "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "dev": true, @@ -1403,6 +1432,19 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/camelcase": { "version": "6.3.0", "dev": true, @@ -1446,6 +1488,24 @@ "node": ">=4" } }, + "node_modules/chai-http": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.3.0.tgz", + "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", + "dev": true, + "dependencies": { + "@types/chai": "4", + "@types/superagent": "^3.8.3", + "cookiejar": "^2.1.1", + "is-ip": "^2.0.0", + "methods": "^1.1.2", + "qs": "^6.5.1", + "superagent": "^3.7.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "dev": true, @@ -1555,6 +1615,12 @@ "dev": true, "license": "MIT" }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, @@ -1581,6 +1647,18 @@ "node": ">= 0.6" } }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -1721,6 +1799,12 @@ "node": ">=4" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, "node_modules/fill-range": { "version": "7.0.1", "dev": true, @@ -1813,6 +1897,16 @@ "node": ">= 6" } }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/fromentries": { "version": "1.3.2", "dev": true, @@ -1837,6 +1931,12 @@ "dev": true, "license": "ISC" }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "dev": true, @@ -1861,6 +1961,20 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "dev": true, @@ -1931,6 +2045,18 @@ "node": ">=4.x" } }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -1939,6 +2065,18 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasha": { "version": "5.2.2", "dev": true, @@ -1997,6 +2135,15 @@ "dev": true, "license": "ISC" }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -2035,6 +2182,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", + "integrity": "sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g==", + "dev": true, + "dependencies": { + "ip-regex": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -2086,6 +2245,12 @@ "node": ">=0.10.0" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "dev": true, @@ -2384,6 +2549,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "license": "MIT", @@ -2672,6 +2858,15 @@ "node": ">=6" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "dev": true, @@ -2741,6 +2936,11 @@ "node": ">=8" } }, + "node_modules/parse-http-header": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", + "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" + }, "node_modules/path-exists": { "version": "4.0.0", "dev": true, @@ -2862,6 +3062,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/process-on-spawn": { "version": "1.0.0", "dev": true, @@ -2881,6 +3087,21 @@ "node": ">= 8" } }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/randombytes": { "version": "2.1.0", "dev": true, @@ -2889,6 +3110,27 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "dev": true, @@ -3004,6 +3246,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "dev": true, @@ -3038,6 +3294,21 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/string-width": { "version": "4.2.3", "dev": true, @@ -3081,6 +3352,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "dev": true, + "dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/superagent/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/supports-color": { "version": "8.1.1", "dev": true, @@ -3191,6 +3507,12 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/uuid": { "version": "8.3.2", "license": "MIT", @@ -3325,7 +3647,8 @@ "axios": ">=0.21.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "lz-string": "^1.4.4", + "parse-http-header": "^1.0.1" } } }, @@ -4171,6 +4494,34 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@types/chai": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "dev": true + }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, + "@types/node": { + "version": "18.11.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", + "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==", + "dev": true + }, + "@types/superagent": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", + "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "dev": true @@ -4290,6 +4641,16 @@ "write-file-atomic": "^3.0.0" } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "camelcase": { "version": "6.3.0", "dev": true @@ -4311,6 +4672,21 @@ "type-detect": "^4.0.5" } }, + "chai-http": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.3.0.tgz", + "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", + "dev": true, + "requires": { + "@types/chai": "4", + "@types/superagent": "^3.8.3", + "cookiejar": "^2.1.1", + "is-ip": "^2.0.0", + "methods": "^1.1.2", + "qs": "^6.5.1", + "superagent": "^3.7.0" + } + }, "chalk": { "version": "4.1.2", "dev": true, @@ -4380,6 +4756,12 @@ "version": "1.0.1", "dev": true }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "dev": true @@ -4402,6 +4784,18 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "dev": true, @@ -4422,7 +4816,8 @@ "axios": ">=0.21.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "lz-string": "^1.4.4", + "parse-http-header": "^1.0.1" } }, "debug": { @@ -4495,6 +4890,12 @@ "version": "4.0.1", "dev": true }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, "fill-range": { "version": "7.0.1", "dev": true, @@ -4542,6 +4943,12 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "dev": true + }, "fromentries": { "version": "1.3.2", "dev": true @@ -4550,6 +4957,12 @@ "version": "1.0.0", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "dev": true @@ -4562,6 +4975,17 @@ "version": "2.0.0", "dev": true }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, "get-package-type": { "version": "0.1.0", "dev": true @@ -4606,10 +5030,25 @@ "version": "1.10.5", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "dev": true }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, "hasha": { "version": "5.2.2", "dev": true, @@ -4646,6 +5085,12 @@ "version": "2.0.4", "dev": true }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "dev": true, @@ -4668,6 +5113,15 @@ "is-extglob": "^2.1.1" } }, + "is-ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", + "integrity": "sha512-9MTn0dteHETtyUx8pxqMwg5hMBi3pvlyglJ+b79KOCca0po23337LbVV2Hl4xmMvfw++ljnO0/+5G6G+0Szh6g==", + "dev": true, + "requires": { + "ip-regex": "^2.0.0" + } + }, "is-number": { "version": "7.0.0", "dev": true @@ -4692,6 +5146,12 @@ "version": "1.0.2", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "isexe": { "version": "2.0.0", "dev": true @@ -4907,6 +5367,18 @@ "semver": "^6.0.0" } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.52.0" }, @@ -5104,6 +5576,12 @@ } } }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, "once": { "version": "1.4.0", "dev": true, @@ -5146,6 +5624,11 @@ "release-zalgo": "^1.0.0" } }, + "parse-http-header": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", + "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" + }, "path-exists": { "version": "4.0.0", "dev": true @@ -5212,6 +5695,12 @@ "version": "2.7.1", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "process-on-spawn": { "version": "1.0.0", "dev": true, @@ -5223,6 +5712,15 @@ "version": "2.0.1", "dev": true }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, "randombytes": { "version": "2.1.0", "dev": true, @@ -5230,6 +5728,29 @@ "safe-buffer": "^5.1.0" } }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "readdirp": { "version": "3.6.0", "dev": true, @@ -5292,6 +5813,17 @@ "version": "3.0.0", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.7", "dev": true @@ -5316,6 +5848,23 @@ "version": "1.0.3", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "string-width": { "version": "4.2.3", "dev": true, @@ -5340,6 +5889,46 @@ "version": "3.1.1", "dev": true }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, "supports-color": { "version": "8.1.1", "dev": true, @@ -5402,6 +5991,12 @@ "picocolors": "^1.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "uuid": { "version": "8.3.2" }, diff --git a/package.json b/package.json index 7cde190c..c1cc054b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "devDependencies": { "chai": "^4.2.0", + "chai-http": "^4.3.0", "mocha": "^9.1.4", "nock": "^13.2.9", "nyc": "^15.1.0", diff --git a/src/handlers/get-collection-by-id.js b/src/handlers/get-collection-by-id.js index f4d1f9fd..dfc2b88b 100644 --- a/src/handlers/get-collection-by-id.js +++ b/src/handlers/get-collection-by-id.js @@ -7,7 +7,7 @@ const getCollectionById = async (event) => { event = processRequest(event); const id = event.pathParameters.id; const esResponse = await getCollection(id); - const response = opensearchResponse.transform(esResponse); + const response = await opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/src/handlers/get-file-set-by-id.js b/src/handlers/get-file-set-by-id.js index f54a3a70..a59be9f6 100644 --- a/src/handlers/get-file-set-by-id.js +++ b/src/handlers/get-file-set-by-id.js @@ -11,6 +11,6 @@ exports.handler = async (event) => { const id = event.pathParameters.id; const allowPrivate = isFromReadingRoom(event); const esResponse = await getFileSet(id, { allowPrivate }); - const response = opensearchResponse.transform(esResponse); + const response = await opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/src/handlers/get-shared-link-by-id.js b/src/handlers/get-shared-link-by-id.js index 9ce4e8f3..2a4287f4 100644 --- a/src/handlers/get-shared-link-by-id.js +++ b/src/handlers/get-shared-link-by-id.js @@ -16,7 +16,7 @@ exports.handler = async (event) => { body: "Not Found", }; } else { - const response = opensearchResponse.transform(esResponse); + const response = await opensearchResponse.transform(esResponse); return processResponse(event, response); } }; diff --git a/src/handlers/get-work-by-id.js b/src/handlers/get-work-by-id.js index 68c16c56..d8580075 100644 --- a/src/handlers/get-work-by-id.js +++ b/src/handlers/get-work-by-id.js @@ -11,6 +11,6 @@ exports.handler = async (event) => { const id = event.pathParameters.id; const allowPrivate = isFromReadingRoom(event); const esResponse = await getWork(id, { allowPrivate }); - const response = opensearchResponse.transform(esResponse); + const response = await opensearchResponse.transform(esResponse); return processResponse(event, response); }; diff --git a/src/handlers/middleware.js b/src/handlers/middleware.js index 4daf456c..a9524f39 100644 --- a/src/handlers/middleware.js +++ b/src/handlers/middleware.js @@ -1,6 +1,7 @@ const { addCorsHeaders, decodeEventBody, + ensureCharacterEncoding, normalizeHeaders, objectifyCookies, stubEventMembers, @@ -17,7 +18,9 @@ const processRequest = function (event) { }; const processResponse = function (event, response) { - return addCorsHeaders(event, response); + let result = addCorsHeaders(event, response); + result = ensureCharacterEncoding(result, "UTF-8"); + return result; }; module.exports = { processRequest, processResponse }; diff --git a/src/helpers.js b/src/helpers.js index 316ff2d5..74822b2f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,3 +1,4 @@ +const parseHeader = require("parse-http-header"); const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; function addCorsHeaders(event, response) { @@ -14,6 +15,18 @@ function addCorsHeaders(event, response) { return response; } +function ensureCharacterEncoding(response, defaultEncoding = "UTF-8") { + response.headers ||= {}; + response.headers["Content-Type"] ||= "application/json; charset=UTF-8"; + const contentTypeHeader = Object.keys(response.headers).find( + (name) => name.toLocaleLowerCase() == "content-type" + ); + if (!parseHeader(response.headers[contentTypeHeader]).charset) { + response.headers[contentTypeHeader] += `; charset=${defaultEncoding}`; + } + return response; +} + function decodeEventBody(event) { if (!event.isBase64Encoded) return event; event.body = Buffer.from(event.body, "base64").toString("utf8"); @@ -115,6 +128,7 @@ module.exports = { addCorsHeaders, baseUrl, decodeEventBody, + ensureCharacterEncoding, isFromReadingRoom, normalizeHeaders, objectifyCookies, diff --git a/src/package-lock.json b/src/package-lock.json index f8690391..9971c882 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -17,7 +17,8 @@ "axios": ">=0.21.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "lz-string": "^1.4.4", + "parse-http-header": "^1.0.1" } }, "node_modules/@aws-crypto/ie11-detection": { @@ -1199,6 +1200,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/parse-http-header": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", + "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2344,6 +2350,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "parse-http-header": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", + "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", diff --git a/src/package.json b/src/package.json index 5eb63650..9b47c9ea 100644 --- a/src/package.json +++ b/src/package.json @@ -14,6 +14,7 @@ "axios": ">=0.21.1", "cookie": "^0.5.0", "jsonwebtoken": "^8.5.1", - "lz-string": "^1.4.4" + "lz-string": "^1.4.4", + "parse-http-header": "^1.0.1" } } diff --git a/test/integration/get-collection-by-id.test.js b/test/integration/get-collection-by-id.test.js index 11630d6c..7945e36d 100644 --- a/test/integration/get-collection-by-id.test.js +++ b/test/integration/get-collection-by-id.test.js @@ -3,6 +3,7 @@ const chai = require("chai"); const expect = chai.expect; const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); describe("Retrieve collection by id", () => { helpers.saveEnvironment(); @@ -23,7 +24,10 @@ describe("Retrieve collection by id", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.data.id).to.eq("1234"); @@ -66,7 +70,10 @@ describe("Retrieve collection by id", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.type).to.eq("Collection"); expect(resultBody.label.none[0]).to.eq("Collection Title"); diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index 7267cf0e..ceb182d7 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -4,6 +4,7 @@ const chai = require("chai"); const expect = chai.expect; const getCollectionsHandler = require("../../src/handlers/get-collections"); const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); describe("Collections route", () => { helpers.saveEnvironment(); @@ -27,7 +28,10 @@ describe("Collections route", () => { event = baseEvent.render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const { pagination: { query_url }, } = JSON.parse(result.body); @@ -43,7 +47,10 @@ describe("Collections route", () => { event = baseEvent.queryParams({ page: 3, size: 5 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const { pagination: { query_url }, } = JSON.parse(result.body); diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js index 938357dc..1829d4d0 100644 --- a/test/integration/get-doc.test.js +++ b/test/integration/get-doc.test.js @@ -2,6 +2,7 @@ const chai = require("chai"); const expect = chai.expect; +chai.use(require("chai-http")); describe("Doc retrieval routes", () => { helpers.saveEnvironment(); @@ -22,7 +23,10 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("Work"); @@ -102,7 +106,10 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("Collection"); @@ -125,7 +132,10 @@ describe("Doc retrieval routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.data.api_model).to.eq("FileSet"); diff --git a/test/integration/get-shared-link-by-id.test.js b/test/integration/get-shared-link-by-id.test.js index d137e547..577d10b9 100644 --- a/test/integration/get-shared-link-by-id.test.js +++ b/test/integration/get-shared-link-by-id.test.js @@ -2,6 +2,7 @@ const chai = require("chai"); const expect = chai.expect; +chai.use(require("chai-http")); describe("Retrieve shared link by id", () => { helpers.saveEnvironment(); @@ -22,7 +23,10 @@ describe("Retrieve shared link by id", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.data.target_id).to.eq( diff --git a/test/integration/get-similar.test.js b/test/integration/get-similar.test.js index 1c530349..8b204fed 100644 --- a/test/integration/get-similar.test.js +++ b/test/integration/get-similar.test.js @@ -4,6 +4,7 @@ const chai = require("chai"); const expect = chai.expect; const { handler } = require("../../src/handlers/get-similar"); const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); describe("Similar routes", () => { helpers.saveEnvironment(); @@ -47,7 +48,10 @@ describe("Similar routes", () => { const event = baseEvent.render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const { pagination: { query_url }, } = JSON.parse(result.body); @@ -90,7 +94,10 @@ describe("Similar routes", () => { const event = baseEvent.queryParams({ page: 3, size: 3 }).render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.pagination.current_page).to.eq(3); expect(resultBody.pagination.limit).to.eq(3); diff --git a/test/integration/search.test.js b/test/integration/search.test.js index 5e9abd37..c71e9805 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -4,6 +4,7 @@ const chai = require("chai"); const expect = chai.expect; const searchHandlers = require("../../src/handlers/search"); const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); describe("Search routes", () => { helpers.saveEnvironment(); @@ -25,7 +26,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody).to.include.keys(["data", "pagination"]); @@ -45,7 +49,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody).to.include.keys(["data", "pagination"]); @@ -95,7 +102,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.pagination.next_url).not.null; expect(resultBody.pagination.current_page).to.eq(1); @@ -125,7 +135,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); }); it("defaults to page 1", async () => { @@ -140,7 +153,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); }); it("will return a IIIF collection", async () => { @@ -162,7 +178,10 @@ describe("Search routes", () => { .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result.headers).to.include({ "content-type": "application/json" }); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); const resultBody = JSON.parse(result.body); expect(resultBody.type).to.eq("Collection"); }); From 43dcf75e65b5db6737c2e93894c9ca6f9e70b511 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 21:36:36 +0000 Subject: [PATCH 41/61] Fix missing parameters in swagger docs --- docs/docs/spec/openapi.yaml | 18 +++++------------- docs/docs/spec/types.yaml | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index 5fcd13ef..2bd16144 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -143,6 +143,7 @@ paths: tags: - Search parameters: + - $ref: "./types.yaml#/components/parameters/models" - $ref: "./types.yaml#/components/parameters/query" - $ref: "./types.yaml#/components/parameters/searchToken" - $ref: "./types.yaml#/components/parameters/page" @@ -156,19 +157,10 @@ paths: tags: - Search parameters: - - name: models - in: path - description: Comma-delimited list of models to search - required: true - schema: - type: array - items: - type: string - enum: - - collections - - file-sets - - Works - style: simple + - $ref: "./types.yaml#/components/parameters/models" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/as" responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" diff --git a/docs/docs/spec/types.yaml b/docs/docs/spec/types.yaml index 5002f3f1..f7e967ef 100644 --- a/docs/docs/spec/types.yaml +++ b/docs/docs/spec/types.yaml @@ -1,6 +1,20 @@ openapi: 3.0.1 components: parameters: + models: + name: models + in: path + description: Comma-delimited list of models to search + required: true + schema: + type: array + items: + type: string + enum: + - collections + - file-sets + - Works + style: simple id: name: id in: path From 0c369a0926a8ded1e19ad824d229e947a382d9f6 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Thu, 20 Oct 2022 21:43:26 +0000 Subject: [PATCH 42/61] Replace accidentally deleted GET /search route --- template.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/template.yaml b/template.yaml index b4874343..a40637a4 100644 --- a/template.yaml +++ b/template.yaml @@ -285,6 +285,12 @@ Resources: Resource: "*" Events: SearchApi: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /search + Method: GET + SearchWithModelsApi: Type: HttpApi Properties: ApiId: !Ref dcApi From ac002c58abd52fe4ab11641acc0cdecb90f879e8 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Fri, 21 Oct 2022 04:52:36 +0000 Subject: [PATCH 43/61] Calculate the effective path better --- src/api/pagination.js | 1 - src/handlers/search-runner.js | 5 +-- src/helpers.js | 10 ++++- test/fixtures/mocks/real-search-event.json | 43 +++++++++++++++++++ test/integration/get-auth-callback.test.js | 1 - test/integration/get-auth-login.test.js | 1 - test/integration/get-auth-whoami.test.js | 6 +-- test/integration/get-collection-by-id.test.js | 3 -- test/integration/get-collections.test.js | 18 ++++++-- test/integration/get-doc.test.js | 9 +--- .../integration/get-shared-link-by-id.test.js | 3 -- test/integration/get-similar.test.js | 1 - test/integration/get-thumbnail.test.js | 3 -- test/integration/options-request.test.js | 1 - test/integration/search.test.js | 13 +----- test/test-helpers/event-builder.js | 16 +++---- test/unit/api/helpers.test.js | 6 +-- test/unit/api/pagination.test.js | 6 +-- test/unit/api/request/pipeline.test.js | 2 +- .../unit/api/response/iiif/collection.test.js | 2 +- 20 files changed, 85 insertions(+), 65 deletions(-) create mode 100644 test/fixtures/mocks/real-search-event.json diff --git a/src/api/pagination.js b/src/api/pagination.js index 41586869..1ac351d2 100644 --- a/src/api/pagination.js +++ b/src/api/pagination.js @@ -56,7 +56,6 @@ class Paginator { async pageInfo(count) { let url = new URL(this.route, this.baseUrl); - if (this.options?.includeToken != false) { url.searchParams.set( "searchToken", diff --git a/src/handlers/search-runner.js b/src/handlers/search-runner.js index ff4b675f..7fe29242 100644 --- a/src/handlers/search-runner.js +++ b/src/handlers/search-runner.js @@ -1,4 +1,4 @@ -const { baseUrl } = require("../helpers"); +const { baseUrl, effectivePath } = require("../helpers"); const { processRequest, processResponse } = require("./middleware"); const { extractRequestedModels, @@ -9,7 +9,6 @@ const { search } = require("../api/opensearch"); const responseTransformer = require("../api/response/transformer"); const { decodeSearchToken, Paginator } = require("../api/pagination"); const RequestPipeline = require("../api/request/pipeline"); -const Path = require("path"); /** * Function to wrap search requests and transform responses @@ -31,7 +30,7 @@ const doSearch = async (event, searchParams = {}) => { } const base = new URL(baseUrl(event)); - const path = Path.relative(base.pathname, event.requestContext?.http?.path); + const path = effectivePath(event); const pager = new Paginator( base.toString(), diff --git a/src/helpers.js b/src/helpers.js index 74822b2f..e8af9005 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,4 +1,5 @@ const parseHeader = require("parse-http-header"); +const path = require("path"); const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/; function addCorsHeaders(event, response) { @@ -51,7 +52,7 @@ function normalizeHeaders(event) { const headers = { ...event.headers }; - for (header in headers) { + for (const header in headers) { const lowerHeader = header.toLowerCase(); if (header != lowerHeader) { const value = headers[header]; @@ -95,6 +96,12 @@ function baseUrl(event) { return result.toString(); } +function effectivePath(event) { + const root = path.join("/", event.requestContext.stage); + const absolute = event.requestContext.http.path; + return path.relative(root, absolute); +} + function objectifyCookies(event) { event.cookieObject = {}; if (!event.cookies) return event; @@ -128,6 +135,7 @@ module.exports = { addCorsHeaders, baseUrl, decodeEventBody, + effectivePath, ensureCharacterEncoding, isFromReadingRoom, normalizeHeaders, diff --git a/test/fixtures/mocks/real-search-event.json b/test/fixtures/mocks/real-search-event.json new file mode 100644 index 00000000..d934f89b --- /dev/null +++ b/test/fixtures/mocks/real-search-event.json @@ -0,0 +1,43 @@ +{ + "version": "2.0", + "routeKey": "GET /search/{models}", + "rawPath": "/v2/search/collections", + "rawQueryString": "", + "cookies": [], + "headers": { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "accept-encoding": "gzip, deflate, br", + "accept-language": "en-US,en;q=0.9", + "content-length": "0", + "host": "api.test.library.northwestern.edu", + "user-agent": "Mocha Test", + "x-forwarded-for": "10.9.8.7", + "x-forwarded-port": "443", + "x-forwarded-proto": "https" + }, + "requestContext": { + "accountId": "625046682746", + "apiId": "pewpewpew", + "domainName": "api.test.library.northwestern.edu", + "domainPrefix": "dcapi", + "http": { + "method": "GET", + "path": "/v2/search/collections", + "protocol": "HTTP/1.1", + "sourceIp": "10.9.8.7", + "userAgent": "Mocha Test" + }, + "requestId": "aVmRZjmGoAMEVuw=", + "routeKey": "GET /search/{models}", + "stage": "v2", + "time": "21/Oct/2022:04:18:42 +0000", + "timeEpoch": 1666325922398 + }, + "pathParameters": { + "models": "collections" + }, + "stageVariables": { + "basePath": "api/v2" + }, + "isBase64Encoded": false +} diff --git a/test/integration/get-auth-callback.test.js b/test/integration/get-auth-callback.test.js index ba6449f2..0a864137 100644 --- a/test/integration/get-auth-callback.test.js +++ b/test/integration/get-auth-callback.test.js @@ -21,7 +21,6 @@ describe("auth callback", function () { const event = helpers .mockEvent("GET", "/auth/callback") - .pathPrefix("/api/v2") .headers({ Cookie: "nusso=bnVzc28=;redirectUrl=aHR0cHM6Ly9leGFtcGxlLmNvbQ==;", }) diff --git a/test/integration/get-auth-login.test.js b/test/integration/get-auth-login.test.js index 2a190ac4..7bf38b09 100644 --- a/test/integration/get-auth-login.test.js +++ b/test/integration/get-auth-login.test.js @@ -20,7 +20,6 @@ describe("auth login", function () { const event = helpers .mockEvent("GET", "/auth/login") - .pathPrefix("/api/v2") .queryParams({ goto: "https://test-goto.com" }) .render(); diff --git a/test/integration/get-auth-whoami.test.js b/test/integration/get-auth-whoami.test.js index 3fc44cca..75a03c41 100644 --- a/test/integration/get-auth-whoami.test.js +++ b/test/integration/get-auth-whoami.test.js @@ -12,7 +12,6 @@ describe("auth whoami", function () { const event = helpers .mockEvent("GET", "/auth/whoami") - .pathPrefix("/api/v2") .headers({ Cookie: "dcApiV2Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaXNwbGF5TmFtZSI6IlNvbWUgT25lIiwiaWF0IjoxNjY1NDE3NzYzfQ.Nwi8dJnc7w201ZtO5de5zYmU-F5gEalkmHZ5pR1VXms;", @@ -27,10 +26,7 @@ describe("auth whoami", function () { it("rejects an invalid or missing NUSSO token", async () => { process.env.API_TOKEN_SECRET = "abc123"; - const event = helpers - .mockEvent("GET", "/auth/whoami") - .pathPrefix("/api/v2") - .render(); + const event = helpers.mockEvent("GET", "/auth/whoami").render(); const result = await getAuthWhoamiHandler.handler(event); expect(result.statusCode).to.eq(401); diff --git a/test/integration/get-collection-by-id.test.js b/test/integration/get-collection-by-id.test.js index 7945e36d..67cc8f2b 100644 --- a/test/integration/get-collection-by-id.test.js +++ b/test/integration/get-collection-by-id.test.js @@ -19,7 +19,6 @@ describe("Retrieve collection by id", () => { const event = helpers .mockEvent("GET", "/collections/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -40,7 +39,6 @@ describe("Retrieve collection by id", () => { const event = helpers .mockEvent("GET", "/collections/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -64,7 +62,6 @@ describe("Retrieve collection by id", () => { const event = helpers .mockEvent("GET", "/collections/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .queryParams({ as: "iiif" }) .render(); diff --git a/test/integration/get-collections.test.js b/test/integration/get-collections.test.js index ceb182d7..181ebddb 100644 --- a/test/integration/get-collections.test.js +++ b/test/integration/get-collections.test.js @@ -12,9 +12,7 @@ describe("Collections route", () => { describe("GET /collections", () => { const handler = getCollectionsHandler.handler; - const baseEvent = helpers - .mockEvent("GET", "/collections") - .pathPrefix("/api/v2"); + const baseEvent = helpers.mockEvent("GET", "/collections"); let event; const makeQuery = (params) => new RequestPipeline({ query: { match_all: {} }, ...params }) @@ -58,5 +56,19 @@ describe("Collections route", () => { expect(url.searchParams.has("searchToken")).to.be.false; expect(url.searchParams.get("size")).to.eq("5"); }); + + it("produces a correct query_url from a mounted API Gateway route", async () => { + mock + .post("/dc-v2-collection/_search", makeQuery({ size: 10, from: 0 })) + .reply(200, helpers.testFixture("mocks/collections.json")); + event = JSON.parse(helpers.testFixture("mocks/real-search-event.json")); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + const { + pagination: { query_url }, + } = JSON.parse(result.body); + const url = new URL(query_url); + expect(url.pathname).to.eq("/api/v2/search/collections"); + }); }); }); diff --git a/test/integration/get-doc.test.js b/test/integration/get-doc.test.js index 1829d4d0..7d048bf4 100644 --- a/test/integration/get-doc.test.js +++ b/test/integration/get-doc.test.js @@ -18,7 +18,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -40,7 +39,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -54,7 +52,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -68,7 +65,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -76,14 +72,13 @@ describe("Doc retrieval routes", () => { }); it("returns a private work to allowed IPs", async () => { - process.env.READING_ROOM_IPS = "127.0.0.1"; + process.env.READING_ROOM_IPS = "10.9.8.7"; mock .get("/dc-v2-work/_doc/1234") .reply(200, helpers.testFixture("mocks/private-work-1234.json")); const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -101,7 +96,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/collections/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -127,7 +121,6 @@ describe("Doc retrieval routes", () => { const event = helpers .mockEvent("GET", "/file-sets/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); diff --git a/test/integration/get-shared-link-by-id.test.js b/test/integration/get-shared-link-by-id.test.js index 577d10b9..fb7ffb62 100644 --- a/test/integration/get-shared-link-by-id.test.js +++ b/test/integration/get-shared-link-by-id.test.js @@ -18,7 +18,6 @@ describe("Retrieve shared link by id", () => { const event = helpers .mockEvent("GET", "/resolve/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .render(); const result = await handler(event); @@ -41,7 +40,6 @@ describe("Retrieve shared link by id", () => { const event = helpers .mockEvent("GET", "/shared_links/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 5678 }) .render(); const result = await handler(event); @@ -58,7 +56,6 @@ describe("Retrieve shared link by id", () => { const event = helpers .mockEvent("GET", "/shared_links/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 9101112 }) .render(); const result = await handler(event); diff --git a/test/integration/get-similar.test.js b/test/integration/get-similar.test.js index 8b204fed..6e9086b1 100644 --- a/test/integration/get-similar.test.js +++ b/test/integration/get-similar.test.js @@ -11,7 +11,6 @@ describe("Similar routes", () => { const mock = helpers.mockIndex(); let baseEvent = helpers .mockEvent("GET", "/works/{id}/similar") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }); const makeQuery = (params) => new RequestPipeline(params).authFilter().toJson(); diff --git a/test/integration/get-thumbnail.test.js b/test/integration/get-thumbnail.test.js index 366cc4ef..2c356f8d 100644 --- a/test/integration/get-thumbnail.test.js +++ b/test/integration/get-thumbnail.test.js @@ -15,7 +15,6 @@ describe("Thumbnail routes", () => { describe("Collection", () => { const event = helpers .mockEvent("GET", "/collections/{id}/thumbnail") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }); it("retrieves a thumbnail", async () => { @@ -101,7 +100,6 @@ describe("Thumbnail routes", () => { describe("Work", () => { const event = helpers .mockEvent("GET", "/works/{id}/thumbnail") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }); it("retrieves a thumbnail", async () => { @@ -154,7 +152,6 @@ describe("Thumbnail routes", () => { describe("QueryString parameters", () => { const event = helpers .mockEvent("GET", "/works/{id}/thumbnail") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }); it("accepts a proper size", async () => { diff --git a/test/integration/options-request.test.js b/test/integration/options-request.test.js index aaca5045..6e449412 100644 --- a/test/integration/options-request.test.js +++ b/test/integration/options-request.test.js @@ -7,7 +7,6 @@ const optionsHandler = require("../../src/handlers/options-request"); describe("OPTIONS handler", async () => { const event = helpers .mockEvent("OPTIONS", "/auth/whoami") - .pathPrefix("/api/v2") .headers({ Origin: "https://dc.library.northwestern.edu/origin-test-path", }) diff --git a/test/integration/search.test.js b/test/integration/search.test.js index c71e9805..b41b36f6 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -21,7 +21,6 @@ describe("Search routes", () => { .reply(200, helpers.testFixture("mocks/search.json")); const event = helpers .mockEvent("POST", "/search") - .pathPrefix("/api/v2") .body(originalQuery) .render(); const result = await handler(event); @@ -42,7 +41,6 @@ describe("Search routes", () => { .reply(200, helpers.testFixture("mocks/search-multiple-targets.json")); const event = helpers .mockEvent("POST", "/search/{models}") - .pathPrefix("/api/v2") .pathParams({ models: "works,collections" }) .body(originalQuery) .base64Encode() @@ -62,7 +60,6 @@ describe("Search routes", () => { it("errors if invalid models specified", async () => { const event = helpers .mockEvent("POST", "/search/{models}") - .pathPrefix("/api/v2") .pathParams({ models: "works,collections,blargh" }) .body(originalQuery) .render(); @@ -95,11 +92,7 @@ describe("Search routes", () => { mock .post("/dc-v2-work/_search", authQuery) .reply(200, helpers.testFixture("mocks/search.json")); - const event = helpers - .mockEvent("GET", "/search") - .pathPrefix("/api/v2") - .queryParams() - .render(); + const event = helpers.mockEvent("GET", "/search").queryParams().render(); const result = await handler(event); expect(result.statusCode).to.eq(200); expect(result).to.have.header( @@ -114,7 +107,6 @@ describe("Search routes", () => { it("Errors on invalid searchToken", async () => { const event = helpers .mockEvent("GET", "/search") - .pathPrefix("/api/v2") .queryParams({ searchToken: "Ceci n'est pas une searchToken" }) .render(); const result = await handler(event); @@ -130,7 +122,6 @@ describe("Search routes", () => { const event = helpers .mockEvent("GET", "/search") - .pathPrefix("/api/v2") .queryParams({ searchToken, page: 1 }) .render(); const result = await handler(event); @@ -148,7 +139,6 @@ describe("Search routes", () => { const event = helpers .mockEvent("GET", "/search") - .pathPrefix("/api/v2") .queryParams({ searchToken }) .render(); const result = await handler(event); @@ -173,7 +163,6 @@ describe("Search routes", () => { const event = helpers .mockEvent("GET", "/search") - .pathPrefix("/api/v2") .queryParams({ as: "iiif" }) .render(); const result = await handler(event); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index db590d8d..c9e30688 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -2,16 +2,15 @@ module.exports = class { constructor(method, route) { const now = new Date(); this._method = method; - this._pathPrefix = ""; this._route = route; this._event = { version: "2.0", routeKey: `${method} ${route}`, - rawPath: route, + rawPath: `/v2/${route}`, rawQueryString: "", headers: { Host: "api.test.library.northwestern.edu", - "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-For": "10.9.8.7, 10.6.5.4", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https", }, @@ -22,9 +21,9 @@ module.exports = class { domainPrefix: "api", http: { method: method, - path: route, + path: `/v2/${route}`, protocol: "HTTP/1.1", - sourceIp: "127.0.0.1", + sourceIp: "10.9.8.7", userAgent: "Mocha Test", }, requestId: "id", @@ -34,15 +33,11 @@ module.exports = class { timeEpoch: Number(now), }, body: "", + stageVariables: { basePath: "api/v2" }, isBase64Encoded: false, }; } - pathPrefix(prefix) { - this._pathPrefix = prefix; - return this; - } - body(body) { switch (typeof body) { case "string": @@ -102,7 +97,6 @@ module.exports = class { result.cookies = cookies.split(/;\s*/); } - result.rawPath = this._pathPrefix + this._route; result.requestContext.http.path = result.rawPath; if (this._pathParams) { diff --git a/test/unit/api/helpers.test.js b/test/unit/api/helpers.test.js index 986c75d7..b73a4545 100644 --- a/test/unit/api/helpers.test.js +++ b/test/unit/api/helpers.test.js @@ -35,7 +35,7 @@ describe("helpers", () => { it("extracts the base URL from an API Gateway event", () => { const event = { routeKey: "GET /route/{param}", - rawPath: "/v2/route/value", + rawPath: "/api/v2/route/value", headers: { host: "abcdefghijz.execute-api.us-east-1.amazonaws.com", "x-forwarded-proto": "https", @@ -46,10 +46,11 @@ describe("helpers", () => { domainPrefix: "abcdefghijz", stage: "v2", }, + stageVariables: "api/v2", }; expect(baseUrl(event)).to.eq( - "https://abcdefghijz.execute-api.us-east-1.amazonaws.com/v2/" + "https://abcdefghijz.execute-api.us-east-1.amazonaws.com/api/v2/" ); }); @@ -117,7 +118,6 @@ describe("helpers", () => { it("properly handles a base path mapping", () => { const event = helpers .mockEvent("GET", "/works/{id}") - .pathPrefix("/api/v2") .pathParams({ id: 1234 }) .stageVariables({ basePath: "api/v2" }) .render(); diff --git a/test/unit/api/pagination.test.js b/test/unit/api/pagination.test.js index 15e2e696..23199a67 100644 --- a/test/unit/api/pagination.test.js +++ b/test/unit/api/pagination.test.js @@ -17,7 +17,7 @@ describe("Paginator", function () { this.beforeEach(() => { pager = new Paginator( - "http://dcapi.library.northwestern.edu/v2/", + "https://api.test.library.northwestern.edu/api/v2/", "search", ["works"], requestBody @@ -34,8 +34,8 @@ describe("Paginator", function () { expect(result.prev_url).to.be.undefined; let url = new URL(result.next_url); - expect(url.host).to.eq("dcapi.library.northwestern.edu"); - expect(url.pathname).to.eq("/v2/search"); + expect(url.host).to.eq("api.test.library.northwestern.edu"); + expect(url.pathname).to.eq("/api/v2/search"); expect(url.searchParams.get("page")).to.eq("2"); }); diff --git a/test/unit/api/request/pipeline.test.js b/test/unit/api/request/pipeline.test.js index fadfe91d..5461c755 100644 --- a/test/unit/api/request/pipeline.test.js +++ b/test/unit/api/request/pipeline.test.js @@ -52,7 +52,7 @@ describe("RequestPipeline", () => { }); it("includes private results from allowed IP addresses", () => { - process.env.READING_ROOM_IPS = "127.0.0.1,192.168.0.1"; + process.env.READING_ROOM_IPS = "10.9.8.7,192.168.0.1"; const result = pipeline.authFilter(event); expect(result.searchContext.size).to.eq(50); expect(result.searchContext.query.bool.must).to.include( diff --git a/test/unit/api/response/iiif/collection.test.js b/test/unit/api/response/iiif/collection.test.js index 1a398ff7..9512420a 100644 --- a/test/unit/api/response/iiif/collection.test.js +++ b/test/unit/api/response/iiif/collection.test.js @@ -9,7 +9,7 @@ describe("IIIF Collection response transformer", () => { let pager; beforeEach(() => { pager = new Paginator( - "http://dcapi.library.northwestern.edu/v2/", + "http://dcapi.library.northwestern.edu/api/v2/", "search", ["works"], { query: { query_string: { query: "genre.label:architecture" } } }, From 7791adf5207bebe9f1080431bbf8ea98933e7694 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Thu, 20 Oct 2022 21:56:51 +0000 Subject: [PATCH 44/61] Modifies the /shared-links/{id} handler to parse a shared links document and get the target work document regardless of visibility/published status. --- src/handlers/get-shared-link-by-id.js | 42 ++++++++++++------- template.yaml | 4 +- test/fixtures/mocks/shared-link-1234.json | 2 +- .../integration/get-shared-link-by-id.test.js | 18 ++++---- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/handlers/get-shared-link-by-id.js b/src/handlers/get-shared-link-by-id.js index 2a4287f4..9ac753e0 100644 --- a/src/handlers/get-shared-link-by-id.js +++ b/src/handlers/get-shared-link-by-id.js @@ -1,5 +1,5 @@ const { processRequest, processResponse } = require("./middleware"); -const { getSharedLink } = require("../api/opensearch"); +const { getSharedLink, getWork } = require("../api/opensearch"); const opensearchResponse = require("../api/response/opensearch"); /** @@ -8,22 +8,32 @@ const opensearchResponse = require("../api/response/opensearch"); exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; - const esResponse = await getSharedLink(id); - if (linkExpired(esResponse)) { - return { - statusCode: 404, - headers: { "content-type": "text/plain" }, - body: "Not Found", - }; - } else { - const response = await opensearchResponse.transform(esResponse); - return processResponse(event, response); - } + const sharedLinkResponse = await getSharedLink(id); + const sharedLinkResponseBody = JSON.parse(sharedLinkResponse.body); + const expirationDate = new Date(sharedLinkResponseBody?._source?.expires); + const workId = sharedLinkResponseBody?._source?.target_id; + + if (linkExpired(expirationDate) || !workId) + return invalidRequest("Not Found"); + + const workResponse = await getWork(workId, { allowPrivate: true }); + if (workResponse.statusCode !== 200) return invalidRequest("Not Found"); + const response = opensearchResponse.transform(workResponse); + return processResponse(event, response); }; -const linkExpired = (response) => { - const body = JSON.parse(response.body); - const expires = new Date(body?._source?.expires); +const invalidRequest = (message) => { + return { + statusCode: 404, + headers: { "content-type": "text/plain" }, + body: JSON.stringify({ message: message }), + }; +}; + +const linkExpired = (expirationDate) => { + return !isValid(expirationDate) || expirationDate <= new Date(); +}; - return expires <= new Date(); +const isValid = (date) => { + return date instanceof Date && !isNaN(date.getTime()); }; diff --git a/template.yaml b/template.yaml index a40637a4..cbc325bf 100644 --- a/template.yaml +++ b/template.yaml @@ -10,7 +10,7 @@ Globals: CodeUri: ./src Runtime: nodejs16.x Architectures: - - x86_64 + - x86_64 MemorySize: 128 Timeout: 3 Environment: @@ -327,7 +327,7 @@ Resources: Type: HttpApi Properties: ApiId: !Ref dcApi - Path: /resolve/{id} + Path: /shared-links/{id} Method: GET dcApi: Type: AWS::Serverless::HttpApi diff --git a/test/fixtures/mocks/shared-link-1234.json b/test/fixtures/mocks/shared-link-1234.json index c2838092..4af005a3 100644 --- a/test/fixtures/mocks/shared-link-1234.json +++ b/test/fixtures/mocks/shared-link-1234.json @@ -6,7 +6,7 @@ "found": true, "_source": { "target_index": "meadow", - "target_id": "23255308-53f4-4c96-a268-aefa596d9d21", + "target_id": "1234", "shared_link_id": "b47a0a1c-ca52-424f-877f-a2ab9f1c0735", "expires": "2028-10-26T16:47:06Z" } diff --git a/test/integration/get-shared-link-by-id.test.js b/test/integration/get-shared-link-by-id.test.js index fb7ffb62..eea77329 100644 --- a/test/integration/get-shared-link-by-id.test.js +++ b/test/integration/get-shared-link-by-id.test.js @@ -8,7 +8,7 @@ describe("Retrieve shared link by id", () => { helpers.saveEnvironment(); const mock = helpers.mockIndex(); - describe("GET /resolve/{id}", () => { + describe("GET /shared-links/{id}", () => { const { handler } = require("../../src/handlers/get-shared-link-by-id"); it("retrieves a single shared link document", async () => { @@ -16,21 +16,19 @@ describe("Retrieve shared link by id", () => { .get("/shared_links/_doc/1234") .reply(200, helpers.testFixture("mocks/shared-link-1234.json")); + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/private-work-1234.json")); + const event = helpers - .mockEvent("GET", "/resolve/{id}") + .mockEvent("GET", "/shared-links/{id}") .pathParams({ id: 1234 }) .render(); const result = await handler(event); expect(result.statusCode).to.eq(200); - expect(result).to.have.header( - "content-type", - /application\/json;.*charset=UTF-8/ - ); - const resultBody = JSON.parse(result.body); - expect(resultBody.data.target_id).to.eq( - "23255308-53f4-4c96-a268-aefa596d9d21" - ); + expect(resultBody.data.api_model).to.eq("Work"); + expect(resultBody.data.visibility).to.eq("Private"); }); it("404s a missing shared link", async () => { From 76d84f96b59b132623bdfb4acf944e8ad51a8750 Mon Sep 17 00:00:00 2001 From: "Adam J. Arling" Date: Thu, 20 Oct 2022 20:14:16 +0000 Subject: [PATCH 45/61] Added as-iiif to Work response giving manifest shape Co-authored-by: Karen Shaw Co-authored-by: Mat Jordan --- README.md | 15 + package-lock.json | 316 +++++++++++++++ src/api/response/iiif/manifest.js | 261 +++++++++++++ .../response/iiif/presentation-api/items.js | 58 +++ .../iiif/presentation-api/metadata.js | 125 ++++++ src/aws/environment.js | 5 + src/handlers/get-work-by-id.js | 14 +- src/package-lock.json | 315 +++++++++++++++ src/package.json | 1 + test/fixtures/mocks/work-1234.json | 366 +++++++++++++++++- test/fixtures/mocks/work-video-5678.json | 148 +++++++ test/integration/get-work-by-id.js | 79 ++++ test/unit/api/response/iiif/manifest.test.js | 185 +++++++++ .../iiif/presentation-api/items.test.js | 104 +++++ .../iiif/presentation-api/metadata.test.js | 33 ++ 15 files changed, 2021 insertions(+), 4 deletions(-) create mode 100644 src/api/response/iiif/manifest.js create mode 100644 src/api/response/iiif/presentation-api/items.js create mode 100644 src/api/response/iiif/presentation-api/metadata.js create mode 100644 test/fixtures/mocks/work-video-5678.json create mode 100644 test/integration/get-work-by-id.js create mode 100644 test/unit/api/response/iiif/manifest.test.js create mode 100644 test/unit/api/response/iiif/presentation-api/items.test.js create mode 100644 test/unit/api/response/iiif/presentation-api/metadata.test.js diff --git a/README.md b/README.md index af47d1d2..dfab7f4b 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,18 @@ The API will be available at: - `http://localhost:3000` (from your dev environment) - `http://USER_PREFIX.dev.library.northwestern.edu:3000` (from elsewhere) - Don't forget to [open port 3000](https://github.com/nulib/aws-developer-environment#convenience-scripts) if you want to access it remotely + +## Running the API locally via our AWS dev domain + +This will make the local environment live at: https://[NAME].dev.rdc.library.northwestern.edu:3002/search + +``` +docker run --rm -it -d \ + -e "UPSTREAM_DOMAIN=172.17.0.1" \ + -e "UPSTREAM_PORT=3000" \ + -e "PROXY_DOMAIN=$DEV_PREFIX.dev.rdc.library.northwestern.edu" \ + -v /home/ec2-user/.dev_cert/dev.rdc.cert.pem:/etc/nginx/certs/cert.pem \ + -v /home/ec2-user/.dev_cert/dev.rdc.key.pem:/etc/nginx/certs/key.pem \ + -p 3002:443 \ + outrigger/https-proxy:1.0 +``` diff --git a/package-lock.json b/package-lock.json index 0ebb3c94..ca2c53ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1029,6 +1029,17 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.18.10", "dev": true, @@ -1075,6 +1086,47 @@ "node": ">=6.9.0" } }, + "node_modules/@iiif/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iiif/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-p2pqngR/L+ML+LS8Ah70CTITgW192fmWXxOovSapQnI5rKN6daYrbjl5UuIK9R+n0lFFlJwjW5+tA96E+V39hw==", + "dependencies": { + "@iiif/presentation-2": "^1.0.4", + "@iiif/presentation-3": "^1.1.3", + "@types/geojson": "^7946.0.8" + } + }, + "node_modules/@iiif/presentation-2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@iiif/presentation-2/-/presentation-2-1.0.4.tgz", + "integrity": "sha512-hJakpq62VBajesLJrYPtFm6hcn6c/HkKP7CmKZ5atuzu40m0nifWYsqigR1l9sZGvhhHb/DRshPmiW/0GNrJoA==", + "peerDependencies": { + "@iiif/presentation-3": "*" + } + }, + "node_modules/@iiif/presentation-3": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iiif/presentation-3/-/presentation-3-1.1.3.tgz", + "integrity": "sha512-Ek+25nkQouo0pXAqCsWYbAeS4jLDEBQA7iul2jzgnvoJrucxDQN2lXyNLgOUDRqpTdSqJ69iz5lm6DLaxil+Nw==", + "dependencies": { + "@types/geojson": "^7946.0.7" + } + }, + "node_modules/@iiif/vault": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@iiif/vault/-/vault-0.9.19.tgz", + "integrity": "sha512-YoRp4waV1mGMOJaaTpzn/YnZKRnHZdbXwCt2LbCA89CZhtlTh6QUhs04KWNrGxrxnk/Wtt67/U4SyZgD10g0NQ==", + "dependencies": { + "@iiif/parser": "1.*", + "@iiif/presentation-2": "1.*", + "@iiif/presentation-3": "1.*", + "mitt": "^3.0.0", + "node-fetch": "^3.1.1", + "redux": "^4.1.2", + "tiny-invariant": "^1.2.0", + "typesafe-actions": "^5.1.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -1228,6 +1280,11 @@ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, "node_modules/@types/node": { "version": "18.11.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", @@ -1672,6 +1729,14 @@ "node": ">= 8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "engines": { + "node": ">= 12" + } + }, "node_modules/dc-api": { "resolved": "src", "link": true @@ -1805,6 +1870,28 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.0.1", "dev": true, @@ -1897,6 +1984,17 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/formidable": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", @@ -2105,6 +2203,16 @@ "dev": true, "license": "MIT" }, + "node_modules/iiif-builder": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iiif-builder/-/iiif-builder-1.0.6.tgz", + "integrity": "sha512-nNmDDBmbH97Z3HxJ969OBsDaUXgM0cYDZa4/7mH6D8LdzuFHy/4cDzmRQKYdg5BztCD+Ec5fNPDgfvKekwJ/vw==", + "dependencies": { + "@iiif/parser": "1.x", + "@iiif/presentation-3": "1.x", + "@iiif/vault": "0.9.x || 1.x" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "dev": true, @@ -2598,6 +2706,11 @@ "node": ">=10" } }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, "node_modules/mocha": { "version": "9.2.2", "dev": true, @@ -2669,6 +2782,41 @@ "node": ">= 10.13" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-preload": { "version": "0.2.1", "dev": true, @@ -3142,6 +3290,19 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + }, "node_modules/release-zalgo": { "version": "1.0.0", "dev": true, @@ -3435,6 +3596,11 @@ "node": "*" } }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "dev": true, @@ -3482,6 +3648,14 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typesafe-actions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typesafe-actions/-/typesafe-actions-5.1.0.tgz", + "integrity": "sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.5", "dev": true, @@ -3520,6 +3694,14 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -3646,6 +3828,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "cookie": "^0.5.0", + "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1" @@ -4367,6 +4550,14 @@ "version": "7.18.11", "dev": true }, + "@babel/runtime": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.18.10", "dev": true, @@ -4401,6 +4592,45 @@ "to-fast-properties": "^2.0.0" } }, + "@iiif/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iiif/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-p2pqngR/L+ML+LS8Ah70CTITgW192fmWXxOovSapQnI5rKN6daYrbjl5UuIK9R+n0lFFlJwjW5+tA96E+V39hw==", + "requires": { + "@iiif/presentation-2": "^1.0.4", + "@iiif/presentation-3": "^1.1.3", + "@types/geojson": "^7946.0.8" + } + }, + "@iiif/presentation-2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@iiif/presentation-2/-/presentation-2-1.0.4.tgz", + "integrity": "sha512-hJakpq62VBajesLJrYPtFm6hcn6c/HkKP7CmKZ5atuzu40m0nifWYsqigR1l9sZGvhhHb/DRshPmiW/0GNrJoA==", + "requires": {} + }, + "@iiif/presentation-3": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iiif/presentation-3/-/presentation-3-1.1.3.tgz", + "integrity": "sha512-Ek+25nkQouo0pXAqCsWYbAeS4jLDEBQA7iul2jzgnvoJrucxDQN2lXyNLgOUDRqpTdSqJ69iz5lm6DLaxil+Nw==", + "requires": { + "@types/geojson": "^7946.0.7" + } + }, + "@iiif/vault": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@iiif/vault/-/vault-0.9.19.tgz", + "integrity": "sha512-YoRp4waV1mGMOJaaTpzn/YnZKRnHZdbXwCt2LbCA89CZhtlTh6QUhs04KWNrGxrxnk/Wtt67/U4SyZgD10g0NQ==", + "requires": { + "@iiif/parser": "1.*", + "@iiif/presentation-2": "1.*", + "@iiif/presentation-3": "1.*", + "mitt": "^3.0.0", + "node-fetch": "^3.1.1", + "redux": "^4.1.2", + "tiny-invariant": "^1.2.0", + "typesafe-actions": "^5.1.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -4506,6 +4736,11 @@ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, + "@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, "@types/node": { "version": "18.11.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", @@ -4805,6 +5040,11 @@ "which": "^2.0.1" } }, + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" + }, "dc-api": { "version": "file:src", "requires": { @@ -4815,6 +5055,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "cookie": "^0.5.0", + "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1" @@ -4896,6 +5137,15 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "fill-range": { "version": "7.0.1", "dev": true, @@ -4943,6 +5193,14 @@ "mime-types": "^2.1.12" } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, "formidable": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", @@ -5065,6 +5323,16 @@ "version": "2.0.2", "dev": true }, + "iiif-builder": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iiif-builder/-/iiif-builder-1.0.6.tgz", + "integrity": "sha512-nNmDDBmbH97Z3HxJ969OBsDaUXgM0cYDZa4/7mH6D8LdzuFHy/4cDzmRQKYdg5BztCD+Ec5fNPDgfvKekwJ/vw==", + "requires": { + "@iiif/parser": "1.x", + "@iiif/presentation-3": "1.x", + "@iiif/vault": "0.9.x || 1.x" + } + }, "imurmurhash": { "version": "0.1.4", "dev": true @@ -5395,6 +5663,11 @@ "brace-expansion": "^1.1.7" } }, + "mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, "mocha": { "version": "9.2.2", "dev": true, @@ -5442,6 +5715,21 @@ "propagate": "^2.0.0" } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "node-preload": { "version": "0.2.1", "dev": true, @@ -5758,6 +6046,19 @@ "picomatch": "^2.2.1" } }, + "redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + }, "release-zalgo": { "version": "1.0.0", "dev": true, @@ -5954,6 +6255,11 @@ } } }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "to-fast-properties": { "version": "2.0.0", "dev": true @@ -5983,6 +6289,11 @@ "is-typedarray": "^1.0.0" } }, + "typesafe-actions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typesafe-actions/-/typesafe-actions-5.1.0.tgz", + "integrity": "sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==" + }, "update-browserslist-db": { "version": "1.0.5", "dev": true, @@ -6000,6 +6311,11 @@ "uuid": { "version": "8.3.2" }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" + }, "which": { "version": "2.0.2", "dev": true, diff --git a/src/api/response/iiif/manifest.js b/src/api/response/iiif/manifest.js new file mode 100644 index 00000000..e051f121 --- /dev/null +++ b/src/api/response/iiif/manifest.js @@ -0,0 +1,261 @@ +const { IIIFBuilder } = require("iiif-builder"); +const { dcApiEndpoint, dcUrl } = require("../../../aws/environment"); +const { + buildAnnotationBody, + buildImageResourceId, + buildImageService, + isAudioVideo, + isImage, +} = require("./presentation-api/items"); +const { metadataLabelFields } = require("./presentation-api/metadata"); + +function transform(response) { + if (response.statusCode === 200) { + const builder = new IIIFBuilder(); + const openSearchResponse = JSON.parse(response.body); + const source = openSearchResponse._source; + + const manifestId = `${dcApiEndpoint()}/works/${source.id}?as=iiif`; + + const annotationsToTagOnAtEnd = []; + + const normalizedFlatManifestObj = builder.createManifest( + manifestId, + (manifest) => { + function buildCanvasFromFileSet(fileSet, index, isAuxiliary) { + const canvasId = `${manifestId}/canvas/${fileSet.role.toLowerCase()}/${index}`; + manifest.createCanvas(canvasId, (canvas) => { + if (isAudioVideo(source.work_type)) + canvas.duration = fileSet.duration || 1; + + canvas.height = fileSet.height || 100; + canvas.width = fileSet.width || 100; + + canvas.addLabel(fileSet.label, "none"); + + /** Add thumbnail for Canvas */ + if (fileSet.representative_image_url) { + const canvasThumbnail = { + id: buildImageResourceId(fileSet.representative_image_url), + type: "Image", + width: 300, + height: 300, + format: "image/jpeg", + service: buildImageService(fileSet.representative_image_url), + }; + canvas.addThumbnail(canvasThumbnail); + } + + const annotationId = `${canvasId}/annotation/0`; + canvas.createAnnotation(annotationId, { + id: annotationId, + type: "Annotation", + motivation: "painting", + body: buildAnnotationBody( + fileSet, + isAuxiliary ? "Image" : source.work_type + ), + }); + + if (!isAuxiliary && fileSet.webvtt) { + const annotations = { + id: `${canvasId}/annotations/page/0`, + type: "AnnotationPage", + items: [ + { + id: `${canvasId}/annotations/page/0/a0`, + type: "Annotation", + motivation: "supplementing", + body: { + id: fileSet.webvtt, + type: "Text", + format: "text/vtt", + label: { + en: ["Chapters"], + }, + language: "none", + }, + target: canvasId, + }, + ], + }; + annotationsToTagOnAtEnd.push(annotations); + } + }); + } + + /** Build out manifest descriptive properties */ + manifest.addLabel(source.title, "none"); + source.description.length > 0 && + manifest.addSummary(source.description, "none"); + + /** Build metadata property */ + metadataLabelFields(source).forEach((item) => { + if (item.value && item.value.length > 0) { + manifest.addMetadata({ none: [item.label] }, { none: item.value }); + } + }); + + /** Add required statement */ + let requiredStatement = [ + "Courtesy of Northwestern University Libraries", + ]; + manifest.setRequiredStatement({ + label: { none: ["Attribution"] }, + value: { + none: source.terms_of_use + ? requiredStatement.concat(source.terms_of_use) + : requiredStatement, + }, + }); + + /** Add rights using rights statement */ + source.rights_statement?.id && + manifest.setRights(source.rights_statement.id); + + /** Add thumbnail */ + const thumbnail = { + id: source.thumbnail, + type: "Image", + width: 300, + height: 300, + format: "image/jpeg", + }; + manifest.addThumbnail(thumbnail); + + /** Add seeAlso reference for api_link */ + const seeAlso = { + id: source.api_link, + type: "Dataset", + format: "application/json", + label: { + none: ["Northwestern University Libraries Digital Collections API"], + }, + }; + manifest.addSeeAlso(seeAlso); + + /** Add homepage */ + const homepage = { + id: `${dcUrl()}/items/${source.id}`, + type: "Text", + format: "text/html", + label: { + none: [ + "Homepage at Northwestern University Libraries Digital Collections", + ], + }, + }; + manifest.setHomepage(homepage); + + /** Add partOf */ + const collectionEndpoint = `${dcApiEndpoint()}/collections/${ + source.collection.id + }`; + manifest.setPartOf([ + { + id: `${collectionEndpoint}?as=iiif`, + type: "Collection", + label: { none: [source.collection.title] }, + ...(source.collection.description && { + summary: { + none: [source.collection.description], + }, + }), + + /** + * TODO: iiif-builder mangles this property, come back to it + */ + // thumbnail: [ + // { + // id: `${collectionEndpoint}/thumbnail`, + // type: "Image", + // width: 300, + // height: 300, + // format: "image/jpeg", + // }, + // ], + + /** + * TODO: iiif-builder cleanses this property and doesn't include it. + * Come back to it. + */ + homepage: [ + { + id: `${dcUrl()}/collections/${source.collection.id}`, + type: "Text", + format: "text/html", + label: { + none: [ + "Homepage at Northwestern University Libraries Digital Collections", + ], + }, + }, + ], + }, + ]); + + /** Add logo */ + + /** Add provider */ + + /** Add items (Canvases) from a Work's Filesets */ + source.file_sets + .filter((fileSet) => fileSet.role === "Access") + .forEach((fileSet, index) => { + buildCanvasFromFileSet(fileSet, index); + }); + + source.file_sets + .filter((fileSet) => fileSet.role === "Auxiliary") + .forEach((fileSet, index) => { + buildCanvasFromFileSet(fileSet, index, true); + }); + } + ); + + const jsonManifest = builder.toPresentation3({ + id: normalizedFlatManifestObj.id, + type: "Manifest", + }); + + /** + * Workaround to add webVTT annotations + * (iiif-builder package currently doesn't support it) + */ + + annotationsToTagOnAtEnd.forEach((a) => { + const matched = jsonManifest.items.find( + (canvas) => canvas.id === a.items[0].target + ); + + if (matched) { + matched.annotations = [a]; + } + }); + + return { + statusCode: 200, + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + ...jsonManifest, + }), + }; + } + return transformError(response); +} + +function transformError(response) { + const responseBody = { + status: response.statusCode, + error: "TODO", + }; + + return { + statusCode: response.statusCode, + body: JSON.stringify(responseBody), + }; +} + +module.exports = { transform }; diff --git a/src/api/response/iiif/presentation-api/items.js b/src/api/response/iiif/presentation-api/items.js new file mode 100644 index 00000000..5c0b803c --- /dev/null +++ b/src/api/response/iiif/presentation-api/items.js @@ -0,0 +1,58 @@ +/** */ + +function annotationType(workType) { + return workType === "Audio" ? "Sound" : workType; +} + +function buildAnnotationBody(fileSet, workType) { + const body = { + id: buildAnnotationBodyId(fileSet, workType), + type: annotationType(workType), + format: fileSet.mime_type, + height: fileSet.height || 100, + width: fileSet.width || 100, + }; + + if (isImage(workType)) + body.service = buildImageService(fileSet.representative_image_url); + if (isAudioVideo(workType)) body.duration = fileSet.duration; + return body; +} + +function buildAnnotationBodyId(fileSet, workType) { + return isAudioVideo(workType) + ? fileSet.streaming_url + : buildImageResourceId(fileSet.representative_image_url, "600,"); +} + +function buildImageResourceId(uri, size = "!300,300") { + return `${uri}/full/${size}/0/default.jpg`; +} + +function buildImageService(representativeImageUrl) { + return [ + { + id: representativeImageUrl, + profile: "http://iiif.io/api/image/2/level2.json", + type: "ImageService2", + }, + ]; +} + +function isAudioVideo(workType) { + return ["Audio", "Video"].includes(workType); +} + +function isImage(workType) { + return workType === "Image"; +} + +module.exports = { + annotationType, + buildAnnotationBody, + buildAnnotationBodyId, + buildImageResourceId, + buildImageService, + isAudioVideo, + isImage, +}; diff --git a/src/api/response/iiif/presentation-api/metadata.js b/src/api/response/iiif/presentation-api/metadata.js new file mode 100644 index 00000000..2d5c96b6 --- /dev/null +++ b/src/api/response/iiif/presentation-api/metadata.js @@ -0,0 +1,125 @@ +/** Build manifest metadata */ +function formatSingleValuedField(value) { + return value ? [value] : []; +} + +function metadataLabelFields(source) { + return [ + { + label: "Alternate Title", + value: source.alternate_title, + }, + { + label: "Abstract", + value: source.abstract, + }, + { + label: "Caption", + value: source.caption, + }, + { + label: "Contributor", + value: source.contributor.map((item) => item.label_with_role), + }, + { + label: "Creator", + value: source.creator.map((item) => item.label), + }, + { + label: "Cultural Context", + value: source.cultural_context, + }, + { + label: "Date", + value: source.date_created, + }, + { + label: "Department", + value: formatSingleValuedField(source.library_unit), + }, + { + label: "Dimensions", + value: source.physical_description_size, + }, + { + label: "Genre", + value: source.genre.map((item) => item.label), + }, + { + label: "Keyword", + value: source.keywords, + }, + { + label: "Last Modified", + value: formatSingleValuedField(source.modified_date), + }, + { + label: "Language", + value: source.language.map((item) => item.label), + }, + { + label: "Location", + value: source.location?.map((item) => item.label), + }, + { + label: "Materials", + value: source.physical_description_material, + }, + { + label: "Notes", + value: source.notes.map((item) => `${item.note} (${item.type})`), + }, + { + label: "Provenance", + value: source.provenance, + }, + { + label: "Publisher", + value: source.publisher, + }, + { + label: "Related Material", + value: source.related_material, + }, + { + label: "Related URL", + value: source.related_url.map( + (item) => `${item.label}` + ), + }, + { + label: "Rights Holder", + value: source.rights_holder, + }, + { + label: "Scope and Contents", + value: source.scope_and_contents, + }, + { + label: "Series", + value: source.series, + }, + { + label: "Source", + value: source.source, + }, + { + label: "Style Period", + value: source.style_period.map((item) => item.label), + }, + { + label: "Subject", + value: source.subject.map((item) => item.label_with_role), + }, + { + label: "Table of Contents", + value: source.table_of_contents, + }, + { + label: "Technique", + value: source.technique.map((item) => item.label), + }, + ]; +} + +module.exports = { formatSingleValuedField, metadataLabelFields }; diff --git a/src/aws/environment.js b/src/aws/environment.js index c2fc9871..8abed3cf 100644 --- a/src/aws/environment.js +++ b/src/aws/environment.js @@ -13,6 +13,10 @@ function dcApiEndpoint() { return process.env.DC_API_ENDPOINT; } +function dcUrl() { + return process.env.DC_URL; +} + function elasticsearchEndpoint() { return process.env.ELASTICSEARCH_ENDPOINT; } @@ -30,6 +34,7 @@ function region() { module.exports = { apiToken, dcApiEndpoint, + dcUrl, elasticsearchEndpoint, prefix, region, diff --git a/src/handlers/get-work-by-id.js b/src/handlers/get-work-by-id.js index d8580075..a929c8a9 100644 --- a/src/handlers/get-work-by-id.js +++ b/src/handlers/get-work-by-id.js @@ -1,6 +1,8 @@ const { processRequest, processResponse } = require("./middleware"); const { getWork } = require("../api/opensearch"); const { isFromReadingRoom } = require("../helpers"); + +const manifestResponse = require("../api/response/iiif/manifest"); const opensearchResponse = require("../api/response/opensearch"); /** @@ -11,6 +13,16 @@ exports.handler = async (event) => { const id = event.pathParameters.id; const allowPrivate = isFromReadingRoom(event); const esResponse = await getWork(id, { allowPrivate }); - const response = await opensearchResponse.transform(esResponse); + + let response; + const as = event.queryStringParameters.as; + + if (as && as === "iiif") { + // Make it IIIFy + response = manifestResponse.transform(esResponse); + } else { + response = await opensearchResponse.transform(esResponse); + } + return processResponse(event, response); }; diff --git a/src/package-lock.json b/src/package-lock.json index 9971c882..d630d22c 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -16,6 +16,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "cookie": "^0.5.0", + "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1" @@ -1002,6 +1003,63 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, + "node_modules/@babel/runtime": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@iiif/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iiif/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-p2pqngR/L+ML+LS8Ah70CTITgW192fmWXxOovSapQnI5rKN6daYrbjl5UuIK9R+n0lFFlJwjW5+tA96E+V39hw==", + "dependencies": { + "@iiif/presentation-2": "^1.0.4", + "@iiif/presentation-3": "^1.1.3", + "@types/geojson": "^7946.0.8" + } + }, + "node_modules/@iiif/presentation-2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@iiif/presentation-2/-/presentation-2-1.0.4.tgz", + "integrity": "sha512-hJakpq62VBajesLJrYPtFm6hcn6c/HkKP7CmKZ5atuzu40m0nifWYsqigR1l9sZGvhhHb/DRshPmiW/0GNrJoA==", + "peerDependencies": { + "@iiif/presentation-3": "*" + } + }, + "node_modules/@iiif/presentation-3": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iiif/presentation-3/-/presentation-3-1.1.3.tgz", + "integrity": "sha512-Ek+25nkQouo0pXAqCsWYbAeS4jLDEBQA7iul2jzgnvoJrucxDQN2lXyNLgOUDRqpTdSqJ69iz5lm6DLaxil+Nw==", + "dependencies": { + "@types/geojson": "^7946.0.7" + } + }, + "node_modules/@iiif/vault": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@iiif/vault/-/vault-0.9.19.tgz", + "integrity": "sha512-YoRp4waV1mGMOJaaTpzn/YnZKRnHZdbXwCt2LbCA89CZhtlTh6QUhs04KWNrGxrxnk/Wtt67/U4SyZgD10g0NQ==", + "dependencies": { + "@iiif/parser": "1.*", + "@iiif/presentation-2": "1.*", + "@iiif/presentation-3": "1.*", + "mitt": "^3.0.0", + "node-fetch": "^3.1.1", + "redux": "^4.1.2", + "tiny-invariant": "^1.2.0", + "typesafe-actions": "^5.1.0" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1045,6 +1103,14 @@ "node": ">= 0.6" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "engines": { + "node": ">= 12" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1061,6 +1127,28 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", @@ -1093,6 +1181,27 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/iiif-builder": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iiif-builder/-/iiif-builder-1.0.6.tgz", + "integrity": "sha512-nNmDDBmbH97Z3HxJ969OBsDaUXgM0cYDZa4/7mH6D8LdzuFHy/4cDzmRQKYdg5BztCD+Ec5fNPDgfvKekwJ/vw==", + "dependencies": { + "@iiif/parser": "1.x", + "@iiif/presentation-3": "1.x", + "@iiif/vault": "0.9.x || 1.x" + } + }, "node_modules/jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -1195,16 +1304,69 @@ "node": ">= 0.6" } }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/parse-http-header": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1232,11 +1394,24 @@ "semver": "bin/semver" } }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/typesafe-actions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typesafe-actions/-/typesafe-actions-5.1.0.tgz", + "integrity": "sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -1244,6 +1419,14 @@ "bin": { "uuid": "dist/bin/uuid" } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } } }, "dependencies": { @@ -2191,6 +2374,58 @@ } } }, + "@babel/runtime": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@iiif/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iiif/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-p2pqngR/L+ML+LS8Ah70CTITgW192fmWXxOovSapQnI5rKN6daYrbjl5UuIK9R+n0lFFlJwjW5+tA96E+V39hw==", + "requires": { + "@iiif/presentation-2": "^1.0.4", + "@iiif/presentation-3": "^1.1.3", + "@types/geojson": "^7946.0.8" + } + }, + "@iiif/presentation-2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@iiif/presentation-2/-/presentation-2-1.0.4.tgz", + "integrity": "sha512-hJakpq62VBajesLJrYPtFm6hcn6c/HkKP7CmKZ5atuzu40m0nifWYsqigR1l9sZGvhhHb/DRshPmiW/0GNrJoA==", + "requires": {} + }, + "@iiif/presentation-3": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iiif/presentation-3/-/presentation-3-1.1.3.tgz", + "integrity": "sha512-Ek+25nkQouo0pXAqCsWYbAeS4jLDEBQA7iul2jzgnvoJrucxDQN2lXyNLgOUDRqpTdSqJ69iz5lm6DLaxil+Nw==", + "requires": { + "@types/geojson": "^7946.0.7" + } + }, + "@iiif/vault": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@iiif/vault/-/vault-0.9.19.tgz", + "integrity": "sha512-YoRp4waV1mGMOJaaTpzn/YnZKRnHZdbXwCt2LbCA89CZhtlTh6QUhs04KWNrGxrxnk/Wtt67/U4SyZgD10g0NQ==", + "requires": { + "@iiif/parser": "1.*", + "@iiif/presentation-2": "1.*", + "@iiif/presentation-3": "1.*", + "mitt": "^3.0.0", + "node-fetch": "^3.1.1", + "redux": "^4.1.2", + "tiny-invariant": "^1.2.0", + "typesafe-actions": "^5.1.0" + } + }, + "@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2228,6 +2463,11 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2241,6 +2481,15 @@ "safe-buffer": "^5.0.1" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", @@ -2256,6 +2505,24 @@ "mime-types": "^2.1.12" } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "iiif-builder": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iiif-builder/-/iiif-builder-1.0.6.tgz", + "integrity": "sha512-nNmDDBmbH97Z3HxJ969OBsDaUXgM0cYDZa4/7mH6D8LdzuFHy/4cDzmRQKYdg5BztCD+Ec5fNPDgfvKekwJ/vw==", + "requires": { + "@iiif/parser": "1.x", + "@iiif/presentation-3": "1.x", + "@iiif/vault": "0.9.x || 1.x" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -2345,16 +2612,49 @@ "mime-db": "1.52.0" } }, + "mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "parse-http-header": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-http-header/-/parse-http-header-1.0.1.tgz", "integrity": "sha512-xOoH7vzokDoenX4e3c+4ik8lf30kq9Pawq20TH5uq3RURsIJquqFTE0gS7OAEE6nvMQzuP5OXxubYuN7YLsTiw==" }, + "redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2365,15 +2665,30 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "typesafe-actions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typesafe-actions/-/typesafe-actions-5.1.0.tgz", + "integrity": "sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg==" + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" } } } diff --git a/src/package.json b/src/package.json index 9b47c9ea..5e9b3b1c 100644 --- a/src/package.json +++ b/src/package.json @@ -13,6 +13,7 @@ "@aws-sdk/signature-v4": "^3.130.0", "axios": ">=0.21.1", "cookie": "^0.5.0", + "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1" diff --git a/test/fixtures/mocks/work-1234.json b/test/fixtures/mocks/work-1234.json index c967f821..30ce940e 100644 --- a/test/fixtures/mocks/work-1234.json +++ b/test/fixtures/mocks/work-1234.json @@ -3,16 +3,376 @@ "_type": "_doc", "_id": "1234", "_version": 1, + "_seq_no": 719, + "_primary_term": 1, "found": true, "_source": { + "provenance": [ + "Artist; sold to Mr. Blank in 1955; sold to Lancelot in 2017; gifted to Northwestern University in 2019" + ], + "contributor": [ + { + "facet": "http://id.loc.gov/authorities/names/n91114928|ctg|Metallica (Musical group) (Cartographer)", + "id": "http://id.loc.gov/authorities/names/n91114928", + "label": "Metallica (Musical group)", + "label_with_role": "Metallica (Musical group) (Cartographer)", + "role": "Cartographer", + "variants": [] + }, + { + "facet": "http://id.worldcat.org/fast/1204616|abr|South Africa (Abridger)", + "id": "http://id.worldcat.org/fast/1204616", + "label": "South Africa", + "label_with_role": "South Africa (Abridger)", + "role": "Abridger", + "variants": [] + }, + { + "facet": "http://id.worldcat.org/fast/1150166|app|Thistles (Applicant)", + "id": "http://id.worldcat.org/fast/1150166", + "label": "Thistles", + "label_with_role": "Thistles (Applicant)", + "role": "Applicant", + "variants": [] + } + ], + "batch_ids": [ + "a846a5f2-da57-49e6-a138-f5462d113a55", + "97aac3e3-389a-47ac-a7b3-5dd6ffffd558", + "e2529123-6a8c-4f6d-89d5-0257a1b947d4", + "b3d3462d-c03e-4cae-9219-4e38335a25fc", + "1591cc1e-009d-4b39-97a2-7d8743fb957b", + "80a15dc2-92d5-48a4-9aa4-73ecdcb1d130" + ], + "publisher": ["Northwestern University Press"], + "subject": [ + { + "facet": "http://id.worldcat.org/fast/1902713|TOPICAL|Cats on postage stamps (Topical)", + "id": "http://id.worldcat.org/fast/1902713", + "label": "Cats on postage stamps", + "label_with_role": "Cats on postage stamps (Topical)", + "role": "Topical", + "variants": [] + }, + { + "facet": "info:nul/6cba23b5-a91a-4c13-8398-54967b329d48|TOPICAL|Test Record Canary (Topical)", + "id": "info:nul/6cba23b5-a91a-4c13-8398-54967b329d48", + "label": "Test Record Canary", + "label_with_role": "Test Record Canary (Topical)", + "role": "Topical", + "variants": [] + }, + { + "facet": "http://vocab.getty.edu/tgn/2000971|GEOGRAPHICAL|Leelanau (Geographical)", + "id": "http://vocab.getty.edu/tgn/2000971", + "label": "Leelanau", + "label_with_role": "Leelanau (Geographical)", + "role": "Geographical", + "variants": [] + }, + { + "facet": "http://id.worldcat.org/fast/1204587|GEOGRAPHICAL|Michigan--Ann Arbor (Geographical)", + "id": "http://id.worldcat.org/fast/1204587", + "label": "Michigan--Ann Arbor", + "label_with_role": "Michigan--Ann Arbor (Geographical)", + "role": "Geographical", + "variants": [] + } + ], + "scope_and_contents": ["I promise there is scope and content"], + "notes": [ + { + "note": "Here are some notes", + "type": "General Note" + }, + { + "note": "Awards type", + "type": "Awards" + }, + { + "note": "Biographical note", + "type": "Biographical/Historical Note" + }, + { + "note": "creation production credits", + "type": "Creation/Production Credits" + }, + { + "note": "Language note", + "type": "Language Note" + }, + { + "note": "Local Note", + "type": "Local Note" + }, + { + "note": "Performers", + "type": "Performers" + }, + { + "note": "Statement of Responsibility", + "type": "Statement of Responsibility" + }, + { + "note": "Venue/event date", + "type": "Venue/Event Date" + }, + { + "note": "massive add to all pages/check", + "type": "General Note" + } + ], + "related_material": ["See Also: related material"], + "accession_number": "Canary_002", + "modified_date": "2022-10-13T20:56:31.249155Z", + "folder_names": ["Blue folder"], + "series": ["Canaries and How to Care for Them"], + "cultural_context": ["Test Context"], + "language": [ + { + "facet": "http://id.loc.gov/vocabulary/languages/crh||Crimean Tatar", + "id": "http://id.loc.gov/vocabulary/languages/crh", + "label": "Crimean Tatar", + "variants": [] + } + ], + "location": [ + { + "facet": "https://sws.geonames.org/4999069/||Leland Township", + "id": "https://sws.geonames.org/4999069/", + "label": "Leland Township", + "variants": [] + } + ], + "create_date": "2022-03-02T20:38:29.813494Z", + "thumbnail": "https://index.test.library.northwestern.edu/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg", "id": "1234", + "collection": { + "id": "7c50096c-89eb-43e8-b357-5836a788ddeb", + "title": "TEST Canary Records", + "description": "This is the description of the collection" + }, + "abstract": [], + "creator": [ + { + "facet": "http://id.loc.gov/authorities/names/no2011059409||Dessa (Vocalist)", + "id": "http://id.loc.gov/authorities/names/no2011059409", + "label": "Dessa (Vocalist)", + "variants": [ + "Dessa, 1981-", + "Wander, Dessa, 1981-", + "Dessa Darling", + "Wander, Margret" + ] + }, + { + "facet": "http://id.worldcat.org/fast/1152763||Tornadoes", + "id": "http://id.worldcat.org/fast/1152763", + "label": "Tornadoes", + "variants": [] + }, + { + "facet": "http://vocab.getty.edu/aat/300443944||photo editors", + "id": "http://vocab.getty.edu/aat/300443944", + "label": "photo editors", + "variants": [] + }, + { + "facet": "http://id.worldcat.org/fast/1717972||Schober, Franz von, 1796-1882", + "id": "http://id.worldcat.org/fast/1717972", + "label": "Schober, Franz von, 1796-1882", + "variants": [] + } + ], + "rights_holder": ["Artist"], + "box_number": ["88"], + "physical_description_size": ["16 x 24 inches"], + "description": [ + "This is a private record for RepoDev testing on production" + ], + "keywords": ["leaves"], + "indexed_at": "2022-10-14T14:19:42.844994", + "folder_numbers": ["88"], + "genre": [ + { + "facet": "http://id.worldcat.org/fast/1919896||Biographies", + "id": "http://id.worldcat.org/fast/1919896", + "label": "Biographies", + "variants": [] + }, + { + "facet": "http://id.worldcat.org/fast/1019337||Mice", + "id": "http://id.worldcat.org/fast/1019337", + "label": "Mice", + "variants": [] + } + ], + "date_created": ["August 1906 to December 1910", "1958"], + "title": "Canary Record TEST 1", + "physical_description_material": ["Acrylic paint on cement block"], + "csv_metadata_update_jobs": [ + "5753101a-42fa-4838-9b71-f1594a5b1d5f", + "6b46db60-6f6a-45e8-8b8d-ab0029a1e8fe", + "38988b3e-5778-41da-85a5-e16d13cb098a", + "21838181-8d12-4015-8b98-0874061adb98" + ], + "ark": "ark:/99999/fk47h32p0m", + "caption": ["Beebo"], + "status": "Done", + "style_period": [ + { + "facet": "http://vocab.getty.edu/aat/300018478||Qing (dynastic styles and periods)", + "id": "http://vocab.getty.edu/aat/300018478", + "label": "Qing (dynastic styles and periods)", + "variants": [] + } + ], "api_model": "Work", - "published": true, + "catalog_key": ["MS-1984-1982-1989"], + "rights_statement": { + "id": "http://rightsstatements.org/vocab/InC-EDU/1.0/", + "label": "In Copyright - Educational Use Permitted" + }, + "file_sets": [ + { + "duration": null, + "height": 3024, + "id": "076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + "label": "Access File - Tiff", + "mime_type": "image/tiff", + "original_filename": "Squirrel.tif", + "poster_offset": null, + "rank": 0, + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + "role": "Access", + "streaming_url": null, + "webvtt": null, + "width": 4032 + }, + { + "duration": null, + "height": 3024, + "id": "d51cc0b6-562a-4a8f-8443-5e20221c308b", + "label": "Access File - Tiff", + "mime_type": "image/tiff", + "original_filename": "PXL_20211203_142315620.tif", + "poster_offset": null, + "rank": 1073741824, + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/d51cc0b6-562a-4a8f-8443-5e20221c308b", + "role": "Supplemental", + "streaming_url": null, + "webvtt": null, + "width": 4032 + }, + { + "duration": null, + "height": 4032, + "id": "5a4ffcfb-e231-4a59-9e4d-92d1814604fb", + "label": "Preservation File - Tiff", + "mime_type": "image/tiff", + "original_filename": "distillery.tif", + "poster_offset": null, + "rank": 1073741824, + "representative_image_url": null, + "role": "Preservation", + "streaming_url": null, + "webvtt": null, + "width": 3024 + }, + { + "duration": null, + "height": null, + "id": "09617d98-9c67-414e-a0f7-4e69ca99546b", + "label": "Auxiliary File - PNG", + "mime_type": "image/jpeg", + "original_filename": "CoopersHawk.png", + "poster_offset": null, + "rank": 0, + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/09617d98-9c67-414e-a0f7-4e69ca99546b", + "role": "Auxiliary", + "streaming_url": null, + "webvtt": null, + "width": null + }, + { + "duration": null, + "height": null, + "id": "51862c1c-c024-45dc-ab26-694bd8ebc16c", + "label": "Access File - Jpeg", + "mime_type": "image/jpeg", + "original_filename": "Cassettetape.jpg", + "poster_offset": null, + "rank": 1610612736, + "representative_image_url": "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/51862c1c-c024-45dc-ab26-694bd8ebc16c", + "role": "Access", + "streaming_url": null, + "webvtt": null, + "width": null + }, + { + "duration": null, + "height": null, + "id": "a10c1829-265c-44cf-996a-f8c59b16ba97", + "label": "Preservation File - Jpeg", + "mime_type": "image/jpeg", + "original_filename": "Cassettetape.jpg", + "poster_offset": null, + "rank": 0, + "representative_image_url": null, + "role": "Preservation", + "streaming_url": null, + "webvtt": null, + "width": null + } + ], + "library_unit": "Charles Deering McCormick Library of Special Collections", + "technique": [ + { + "facet": "http://vocab.getty.edu/aat/300053228||drypoint (printing process)", + "id": "http://vocab.getty.edu/aat/300053228", + "label": "drypoint (printing process)", + "variants": [] + } + ], + "table_of_contents": ["1. cats; 2. dogs"], "representative_file_set": { "fileSetId": "5678", "url": "https://index.test.library.northwestern.edu/iiif/2/mbk-dev/5678" }, - "thumbnail": "https://index.test.library.northwestern.edu/iiif/2/mbk-dev/5678/square/!300,300/0/default.jpg", - "visibility": "Public" + "related_url": [ + { + "label": "Finding Aid", + "url": "https://findingaids.library.northwestern.edu/" + }, + { + "label": "Research Guide", + "url": "https://www.wbez.org/" + }, + { + "label": "Related Information", + "url": "https://www.nationalgeographic.com/animals/mammals/facts/squirrels" + }, + { + "label": "Hathi Trust Digital Library", + "url": "https://www.hathitrust.org/" + } + ], + "terms_of_use": "Terms ", + "visibility": "Public", + "license": { + "id": "http://www.europeana.eu/portal/rights/rr-r.html", + "label": "All rights reserved", + "scheme": "license" + }, + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/api/v2/works/1234", + "alternate_title": ["This is an alternative title"], + "preservation_level": "Level 1", + "published": true, + "source": ["Mars"], + "iiif_manifest": "https://iiif.stack.rdc-staging.library.northwestern.edu/public/15/6a/8f/8e/-5/49/b-/49/82/-8/6c/c-/37/5b/f0/41/04/ff-manifest.json", + "legacy_identifier": ["555"], + "work_type": "Image", + "identifier": ["555"], + "box_name": ["The name of a box"] } } diff --git a/test/fixtures/mocks/work-video-5678.json b/test/fixtures/mocks/work-video-5678.json new file mode 100644 index 00000000..6b88f17c --- /dev/null +++ b/test/fixtures/mocks/work-video-5678.json @@ -0,0 +1,148 @@ +{ + "_index": "kdid-dev-dc-v2-work-1666658122455", + "_type": "_doc", + "_id": "a3386c0e-e086-45ef-b3c5-9b45ea78211e", + "_version": 7, + "_seq_no": 31, + "_primary_term": 1, + "found": true, + "_source": { + "accession_number": "Video.Work.12342918", + "thumbnail": "https://dcapi.rdc-staging.library.northwestern.edu/works/a3386c0e-e086-45ef-b3c5-9b45ea78211e/thumbnail", + "indexed_at": "2022-10-25T18:22:37.800026", + "iiif_manifest": "https://kdid-dev-pyramids.s3.amazonaws.com/public/iiif3/a3/38/6c/0e/-e/08/6-/45/ef/-b/3c/5-/9b/45/ea/78/21/1e-manifest.json", + "folder_names": [], + "modified_date": "2022-10-25T18:22:13.531600Z", + "series": [], + "source": [], + "physical_description_size": [], + "batch_ids": [], + "publisher": [], + "date_created": ["circa 1990?", "August 1906 to December 1910"], + "csv_metadata_update_jobs": [], + "keywords": [], + "alternate_title": [], + "id": "a3386c0e-e086-45ef-b3c5-9b45ea78211e", + "box_number": [], + "box_name": [], + "create_date": "2022-10-25T00:36:58.251836Z", + "table_of_contents": [], + "project": { + "cycle": null, + "desc": null, + "manager": null, + "name": null, + "proposer": null, + "task_number": null + }, + "style_period": [], + "technique": [], + "api_model": "Work", + "genre": [], + "folder_numbers": [], + "cultural_context": [], + "preservation_level": null, + "creator": [], + "related_url": [], + "visibility": "Public", + "api_link": "https://dcapi.rdc-staging.library.northwestern.edu/works/a3386c0e-e086-45ef-b3c5-9b45ea78211e", + "representative_file_set": { + "id": null, + "url": null + }, + "subject": [], + "legacy_identifier": [], + "published": false, + "collection": { + "description": "This collection features digital copies of 113 antique maps of Africa and accompanying text dating from the mid-16th Century to the early 20th Century. All scanned maps are authentic and originally collected by the Melville J. Herskovits Library of African Studies. Melville J. Herskovits established Northwestern University's Program of African Studies in 1948 (the first of its kind at a major research university in the United States). The Herskovits Library, formally created as a separate library in 1954, has since its inception collected maps that describe Africa from their earliest appearance to the most current. Map area coverage includes the continent, regions (particularly North Africa and Algeria), islands (particularly Madagascar), and a few city plans. All of these maps are loose items, though many have been excised from published atlases. Some of the highlights of the digital collection are: a series of Ptolemic maps of North Africa by Ruscelli, ca. 1565; Forlani: Africa , 1562; Mercator: Africa, 1595; Blaeu: Æthiopia ca. 1650 (a Prester John map); Carey: Africa, 1795 (first map of Africa published in the United States), Arrowsmith: Africa, 1802 (notable for its large dimensions, 124 x 145 cm.), a series of Algerian maps published by the French government in the mid-1800's, and maps by other notable cartographers, such as Hondius, Jansson, de Jode, de L'Isle, Ortelius, Sanson, and de Wit. The original maps are kept and maintained in the map collection in the Government & Geographic Information Collection. We welcome questions, comments, and suggestions concerning any aspect of this digital collection, particularly with regards to provenance. Other antique maps from the Herskovits Library which were not included in this digital collection are either duplicate copies or other editions, such as French government sets covering Algeria.", + "id": "1c2e2200-c12d-4c7f-8b87-a935c349898a", + "title": "16th-Early 20th Century Maps of Africa" + }, + "physical_description_material": [], + "notes": [], + "description": ["Yes!!!"], + "location": [], + "rights_statement": {}, + "identifier": [], + "ingest_project": {}, + "terms_of_use": null, + "abstract": [], + "file_sets": [ + { + "duration": 5.599, + "height": 320, + "id": "5270faab-6575-4808-bad4-336523635a01", + "label": "This is the second file set", + "mime_type": "video/x-m4v", + "original_filename": "small.m4v", + "poster_offset": null, + "rank": 1073741824, + "representative_image_url": null, + "role": "Access", + "streaming_url": "https://kdid-dev-streaming.s3.amazonaws.com/52/70/fa/ab/-6/57/5-/48/08/-b/ad/4-/33/65/23/63/5a/01/5270faab-6575-4808-bad4-336523635a01.m4v", + "webvtt": null, + "width": 560 + }, + { + "duration": null, + "height": null, + "id": "92add7b5-95ff-410e-bac6-6de73dfefb56", + "label": "adasffasdfa", + "mime_type": "application/json", + "original_filename": "details.json", + "poster_offset": null, + "rank": 0, + "representative_image_url": null, + "role": "Supplemental", + "streaming_url": null, + "webvtt": null, + "width": null + }, + { + "duration": null, + "height": 1024, + "id": "0a002b39-43c5-4807-9053-48be93fa67f6", + "label": "coffee!", + "mime_type": "image/tiff", + "original_filename": "coffee.tif", + "poster_offset": null, + "rank": 0, + "representative_image_url": "https://iiif.dev.rdc.library.northwestern.edu/iiif/2/kdid-dev/0a002b39-43c5-4807-9053-48be93fa67f6", + "role": "Auxiliary", + "streaming_url": null, + "webvtt": null, + "width": 1024 + }, + { + "duration": 5.599, + "height": 320, + "id": "7e10e4aa-2ecd-48ef-8424-124d893ff323", + "label": "Yes the is the label edited", + "mime_type": "video/x-m4v", + "original_filename": "small.m4v", + "poster_offset": 2868, + "rank": 0, + "representative_image_url": "https://iiif.dev.rdc.library.northwestern.edu/iiif/2/kdid-dev/posters/7e10e4aa-2ecd-48ef-8424-124d893ff323", + "role": "Access", + "streaming_url": "https://kdid-dev-streaming.s3.amazonaws.com/7e/10/e4/aa/-2/ec/d-/48/ef/-8/42/4-/12/4d/89/3f/f3/23/7e10e4aa-2ecd-48ef-8424-124d893ff323.m4v", + "webvtt": "https://kdid-dev-pyramids.s3.amazonaws.com/public/vtt/7e/10/e4/aa/-2/ec/d-/48/ef/-8/42/4-/12/4d/89/3f/f3/23/7e10e4aa-2ecd-48ef-8424-124d893ff323.vtt", + "width": 560 + } + ], + "language": [], + "title": "The title of the video work", + "ingest_sheet": {}, + "ark": "ark:/99999/fk498553735", + "catalog_key": [], + "provenance": [], + "library_unit": null, + "work_type": "Video", + "contributor": [], + "license": null, + "scope_and_contents": [], + "caption": [], + "status": null, + "rights_holder": [], + "related_material": [] + } +} diff --git a/test/integration/get-work-by-id.js b/test/integration/get-work-by-id.js new file mode 100644 index 00000000..6109e319 --- /dev/null +++ b/test/integration/get-work-by-id.js @@ -0,0 +1,79 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); + +describe("Retrieve work by id", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /works/{id}", () => { + const { handler } = require("../../src/handlers/get-work-by-id"); + + it("retrieves a single work document", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + const event = helpers + .mockEvent("GET", "/work/{id}") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); + + const resultBody = JSON.parse(result.body); + expect(resultBody.data.id).to.eq("1234"); + }); + + it("404s a missing work", async () => { + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const event = helpers + .mockEvent("GET", "/works/{id}") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("returns a single work as a IIIF Manifest", async () => { + // const originalQuery = { + // query: { query_string: { query: "collection.id:1234" } }, + // }; + // const authQuery = new RequestPipeline(originalQuery) + // .authFilter() + // .toJson(); + + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/work-1234.json")); + + const event = helpers + .mockEvent("GET", "/works/{id}") + .pathParams({ id: 1234 }) + .queryParams({ as: "iiif" }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + expect(result).to.have.header( + "content-type", + /application\/json;.*charset=UTF-8/ + ); + const resultBody = JSON.parse(result.body); + expect(resultBody.type).to.eq("Manifest"); + expect(resultBody["@context"]).to.eq( + "http://iiif.io/api/presentation/3/context.json" + ); + expect(resultBody.label.none[0]).to.eq("Canary Record TEST 1"); + }); + }); +}); diff --git a/test/unit/api/response/iiif/manifest.test.js b/test/unit/api/response/iiif/manifest.test.js new file mode 100644 index 00000000..b91b0499 --- /dev/null +++ b/test/unit/api/response/iiif/manifest.test.js @@ -0,0 +1,185 @@ +"use strict"; + +const transformer = require("../../../../../src/api/response/iiif/manifest"); +const chai = require("chai"); +const expect = chai.expect; +const { dcApiEndpoint, dcUrl } = require("../../../../../src/aws/environment"); + +describe("Image Work as IIIF Manifest response transformer", () => { + function getMetadataValueByLabel(metadataArray, targetLabel) { + const foundObj = metadataArray.find( + (item) => item.label.none[0] === targetLabel + ); + return foundObj ? foundObj.value.none : undefined; + } + + async function setup() { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/work-1234.json"), + }; + const source = JSON.parse(response.body)._source; + + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(200); + + return { source, manifest: JSON.parse(result.body) }; + } + + it("transforms an Image work response to minimal Manifest", async () => { + const { manifest } = await setup(); + expect(manifest.type).to.eq("Manifest"); + }); + + it("populates Manifest label", async () => { + const { source, manifest } = await setup(); + expect(manifest.label.none[0]).to.eq(source.title); + }); + + it("populates Manifest summary", async () => { + const { source, manifest } = await setup(); + expect(manifest.summary.none[0]).to.eq(source.description[0]); + }); + + it("populates Manifest metadata", async () => { + const { source, manifest } = await setup(); + expect( + getMetadataValueByLabel(manifest.metadata, "Alternate Title")[0] + ).to.eq(source.alternate_title[0]); + expect(getMetadataValueByLabel(manifest.metadata, "Abstract")).to.be + .undefined; + }); + + it("populates Manifest requiredStatement", async () => { + const { source, manifest } = await setup(); + expect(manifest.requiredStatement.label.none[0]).to.eq("Attribution"); + expect(manifest.requiredStatement.value.none[0]).to.eq( + "Courtesy of Northwestern University Libraries" + ); + expect(manifest.requiredStatement.value.none.includes(source.terms_of_use)) + .to.be.true; + }); + + it("populates Manifest rights", async () => { + const { source, manifest } = await setup(); + expect(manifest.rights).to.eq(source.rights_statement.id); + }); + + it("populates Manifest thumbnail", async () => { + const { source, manifest } = await setup(); + expect(manifest.thumbnail[0].id).to.eq(source.thumbnail); + }); + + it("populates Manifest seeAlso", async () => { + const { source, manifest } = await setup(); + expect(manifest.seeAlso[0].id).to.eq(source.api_link); + expect(manifest.seeAlso[0].type).to.eq("Dataset"); + expect(manifest.seeAlso[0].format).to.eq("application/json"); + expect(manifest.seeAlso[0].label.none[0]).to.eq( + "Northwestern University Libraries Digital Collections API" + ); + }); + + it("populates Manifest homepage", async () => { + const { source, manifest } = await setup(); + expect(manifest.homepage[0].id).to.eq(`${dcUrl()}/items/${source.id}`); + expect(manifest.homepage[0].label.none[0]).to.eq( + "Homepage at Northwestern University Libraries Digital Collections" + ); + }); + + it("populates Manifest partOf", async () => { + const { source, manifest } = await setup(); + const partOf = manifest.partOf[0]; + expect(partOf.id).to.eq( + `${dcApiEndpoint()}/collections/${source.collection.id}?as=iiif` + ); + expect(partOf.type).to.eq("Collection"); + expect(partOf.label.none).to.be.an("array").that.is.not.empty; + expect(partOf.summary.none).to.be.an("array").that.is.not.empty; + }); + + it("populates Manifest items (canvases)", async () => { + const { source, manifest } = await setup(); + expect(manifest.items.length).to.eq(3); + manifest.items.forEach((canvas) => { + expect(canvas.type).to.eq("Canvas"); + }); + expect(manifest.items[0].width).to.eq(source.file_sets[0].width); + expect(manifest.items[0].height).to.eq(source.file_sets[0].height); + expect(manifest.items[0].label.none[0]).to.eq(source.file_sets[0].label); + expect(manifest.items[0].thumbnail[0].id).contains( + source.file_sets[0].representative_image_url + ); + }); + + it("excludes Preservation and Supplemental filesets", async () => { + const { source, manifest } = await setup(); + manifest.items.forEach((canvas) => { + expect(canvas.id).not.contains(["preservation", "supplemental"]); + }); + }); + + it("populates Annotation (painting) for Image fileset", async () => { + const { source, manifest } = await setup(); + const annotation = manifest.items[0].items[0].items[0]; + expect(annotation.type).to.eq("Annotation"); + expect(annotation.motivation).to.eq("painting"); + expect(annotation.target).to.eq(manifest.items[0].id); + expect(annotation.body.id).contains( + source.file_sets[0].representative_image_url + ); + expect(annotation.body.format).to.eq(source.file_sets[0].mime_type); + expect(annotation.body.type).to.eq("Image"); + expect(annotation.body.width).to.eq(source.file_sets[0].width); + expect(annotation.body.height).to.eq(source.file_sets[0].height); + expect(annotation.body.service[0]["@id"]).to.eq( + source.file_sets[0].representative_image_url + ); + }); +}); + +describe("A/V Work as IIIF Manifest response transformer", () => { + async function setup() { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/work-video-5678.json"), + }; + const source = JSON.parse(response.body)._source; + + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(200); + + return { source, manifest: JSON.parse(result.body) }; + } + + it("transforms a Video work response to minimal Manifest", async () => { + const { manifest } = await setup(); + expect(manifest.type).to.eq("Manifest"); + }); + + it("renders duration on AV canvases", async () => { + const { manifest } = await setup(); + expect(manifest.items[0].duration).to.eq(5.599); + }); + + it("renders annotation for type: Sound and Video ", async () => { + const { source, manifest } = await setup(); + const annotation = manifest.items[0].items[0].items[0]; + + expect(annotation.body.duration).to.eq(5.599); + expect(annotation.body.type).to.eq("Video"); + expect(annotation.body.id).to.eq(source.file_sets[0].streaming_url); + }); +}); + +describe("404 network response", () => { + it("returns as expected", async () => { + const response = { + statusCode: 404, + body: helpers.testFixture("mocks/missing-work-1234.json"), + }; + const result = await transformer.transform(response); + expect(result.statusCode).to.eq(404); + }); +}); diff --git a/test/unit/api/response/iiif/presentation-api/items.test.js b/test/unit/api/response/iiif/presentation-api/items.test.js new file mode 100644 index 00000000..dffecc29 --- /dev/null +++ b/test/unit/api/response/iiif/presentation-api/items.test.js @@ -0,0 +1,104 @@ +"use strict"; + +const items = require("../../../../../../src/api/response/iiif/presentation-api/items"); +const chai = require("chai"); +const expect = chai.expect; + +describe("IIIF response presentation API items helpers", () => { + const accessImage = { + duration: null, + height: 3024, + id: "076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + label: "Access File - Tiff", + mime_type: "image/tiff", + original_filename: "Squirrel.tif", + poster_offset: null, + rank: 0, + representative_image_url: + "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + role: "Access", + streaming_url: null, + webvtt: null, + width: 4032, + }; + + // const accessAudio = { + // duration: null, + // height: 3024, + // id: "076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + // label: "Access File - Tiff", + // mime_type: "image/tiff", + // original_filename: "Squirrel.tif", + // poster_offset: null, + // rank: 0, + // representative_image_url: + // "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/076dcbd8-8c57-40e8-bdf7-dc9153c87a36", + // role: "Access", + // streaming_url: null, + // webvtt: null, + // width: 4032, + // }; + + it("annotationType(workType)", () => { + expect(items.annotationType("Audio")).to.eq("Sound"); + expect(items.annotationType("Image")).to.eq("Image"); + expect(items.annotationType("Video")).to.eq("Video"); + }); + + it("buildAnnotationBody(fileSet, workType)", () => { + const body = items.buildAnnotationBody(accessImage, "Image"); + + expect(body.id).contains(accessImage.representative_image_url); + expect(body.width).to.eq(accessImage.width); + expect(body.format).to.eq(accessImage.mime_type); + expect(body.service[0].id).to.eq(accessImage.representative_image_url); + }); + + it("buildAnnotationBody(fileSet, workType)", () => { + const bodyId = items.buildAnnotationBodyId(accessImage, "Image"); + + expect(bodyId).eq( + `${accessImage.representative_image_url}/full/600,/0/default.jpg` + ); + }); + + it('buildImageResourceId(representativeImageUrl,size = "!300,300")', () => { + expect( + items.buildImageResourceId(accessImage.representative_image_url) + ).to.eq( + `${accessImage.representative_image_url}/full/!300,300/0/default.jpg` + ); + expect( + items.buildImageResourceId( + accessImage.representative_image_url, + "1000,1000" + ) + ).to.eq( + `${accessImage.representative_image_url}/full/1000,1000/0/default.jpg` + ); + }); + + it("buildImageService(representativeImageUrl)", () => { + const imageService = items.buildImageService( + accessImage.representative_image_url + )[0]; + + expect(imageService.id).to.eq(accessImage.representative_image_url); + expect(imageService.profile).to.eq( + "http://iiif.io/api/image/2/level2.json" + ); + expect(imageService.type).to.eq("ImageService2"); + }); + + it("isAudioVideo(workType)", () => { + expect(items.isAudioVideo("Audio")).to.be.true; + expect(items.isAudioVideo("Image")).to.be.false; + expect(items.isAudioVideo("Video")).to.be.true; + }); + + it("isImage(workType)", () => { + expect(items.isImage("Audio")).to.be.false; + expect(items.isImage("Image")).to.be.true; + expect(items.isImage("Video")).to.be.false; + }); +}); diff --git a/test/unit/api/response/iiif/presentation-api/metadata.test.js b/test/unit/api/response/iiif/presentation-api/metadata.test.js new file mode 100644 index 00000000..998a03bc --- /dev/null +++ b/test/unit/api/response/iiif/presentation-api/metadata.test.js @@ -0,0 +1,33 @@ +"use strict"; + +const { + formatSingleValuedField, + metadataLabelFields, +} = require("../../../../../../src/api/response/iiif/presentation-api/metadata"); +const chai = require("chai"); +const expect = chai.expect; + +describe("IIIF response presentation API metadata helpers", () => { + const response = { + statusCode: 200, + body: helpers.testFixture("mocks/work-1234.json"), + }; + const source = JSON.parse(response.body)._source; + + it("formatSingleValuedField(value)", () => { + expect(formatSingleValuedField("This value.")) + .to.be.an("array") + .that.does.include("This value."); + expect(formatSingleValuedField(null)).to.be.an("array").that.is.empty; + }); + + it("metadataLabelFields(source)", () => { + const metadata = metadataLabelFields(source); + expect(Array.isArray(metadata)).to.be; + expect(metadata.length).to.eq(28); + metadata.forEach((item) => { + expect(item.label).to.be.a("string"); + expect(item.value).to.be.an("array"); + }); + }); +}); From 00660165832f337e0fc5b955304732924d149b84 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Thu, 27 Oct 2022 20:07:10 +0000 Subject: [PATCH 46/61] Adds thumbnail to IIIF collection if it's an RDC Collection --- src/api/response/iiif/collection.js | 35 +++++++++++++++++++++++------ test/test-helpers/index.js | 5 +++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/api/response/iiif/collection.js b/src/api/response/iiif/collection.js index 32c4425a..a71fab5d 100644 --- a/src/api/response/iiif/collection.js +++ b/src/api/response/iiif/collection.js @@ -1,4 +1,4 @@ -const dcUrl = process.env.DC_URL || "http://placeholder-for-dc-url"; +const { dcApiEndpoint, dcUrl } = require("../../../aws/environment"); async function transform(response, pager) { if (response.statusCode === 200) { @@ -26,7 +26,7 @@ async function buildCollection(responseBody, pageInfo) { }, } = pageInfo; - return { + result = { "@context": ["http://iiif.io/api/presentation/3/context.json"], id: collectionId(pageInfo), type: "Collection", @@ -42,8 +42,29 @@ async function buildCollection(responseBody, pageInfo) { }, }, ], + items: getItems(responseBody?.hits?.hits, pageInfo), }; + + if (pageInfo.options?.parameterOverrides) { + const collectionId = new URL(pageInfo.query_url).pathname + .split("/") + .reverse()[0]; + thumbnailId = new URL( + `${dcApiEndpoint()}/collections/${collectionId}/thumbnail` + ); + result.thumbnail = [ + { + id: thumbnailId, + type: "Image", + format: "image/jpeg", + width: 400, + height: 400, + }, + ]; + } + + return result; } function getItems(hits, pageInfo) { @@ -77,9 +98,9 @@ function homepageUrl(pageInfo) { const collectionId = new URL(pageInfo.query_url).pathname .split("/") .reverse()[0]; - result = new URL(`/collections/${collectionId}`, dcUrl); + result = new URL(`/collections/${collectionId}`, dcUrl()); } else { - result = new URL("/search", dcUrl); + result = new URL("/search", dcUrl()); if (pageInfo.options?.queryStringParameters?.query) result.searchParams.set( "q", @@ -96,7 +117,7 @@ function loadItem(item) { type: "Manifest", homepage: [ { - id: `${dcUrl}/items/${item.id}`, + id: new URL(`/items/${item.id}`, dcUrl()), type: "Text", format: "text/html", label: { @@ -112,12 +133,12 @@ function loadItem(item) { }, thumbnail: [ { - id: [`${item.representative_file_set.url}/full/400,/0/default.jpg`], + id: `${item.representative_file_set.url}/full/400,/0/default.jpg`, service: [ { profile: "http://iiif.io/api/image/2/level2.json", "@context": "http://iiif.io/api/image/2/context.json", - "@id": `${item.representative_file_set.url}`, + "@id": item.representative_file_set.url, }, ], type: "Image", diff --git a/test/test-helpers/index.js b/test/test-helpers/index.js index 97ae6188..68a19cb8 100644 --- a/test/test-helpers/index.js +++ b/test/test-helpers/index.js @@ -6,6 +6,11 @@ const EventBuilder = require("./event-builder.js"); function saveEnvironment() { const env = Object.assign({}, process.env); + beforeEach(function () { + process.env.DC_URL = "https://thisisafakedcurl"; + process.env.DC_API_ENDPOINT = "https://thisisafakeapiurl"; + }); + afterEach(function () { process.env = env; }); From df1dbf29174af705feabaf55b763be6cf9e45aa7 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Thu, 27 Oct 2022 20:49:04 +0000 Subject: [PATCH 47/61] Address PR comments --- src/api/response/iiif/collection.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/api/response/iiif/collection.js b/src/api/response/iiif/collection.js index a71fab5d..2d7a8bee 100644 --- a/src/api/response/iiif/collection.js +++ b/src/api/response/iiif/collection.js @@ -50,9 +50,7 @@ async function buildCollection(responseBody, pageInfo) { const collectionId = new URL(pageInfo.query_url).pathname .split("/") .reverse()[0]; - thumbnailId = new URL( - `${dcApiEndpoint()}/collections/${collectionId}/thumbnail` - ); + const thumbnailId = `${dcApiEndpoint()}/collections/${collectionId}/thumbnail`; result.thumbnail = [ { id: thumbnailId, @@ -133,14 +131,8 @@ function loadItem(item) { }, thumbnail: [ { - id: `${item.representative_file_set.url}/full/400,/0/default.jpg`, - service: [ - { - profile: "http://iiif.io/api/image/2/level2.json", - "@context": "http://iiif.io/api/image/2/context.json", - "@id": item.representative_file_set.url, - }, - ], + id: item.thumbnail, + format: "image/jpeg", type: "Image", width: 400, height: 400, From c99d1c13bffc134656d830c17948948e8ab8c262 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Fri, 28 Oct 2022 18:23:24 +0000 Subject: [PATCH 48/61] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfab7f4b..e038d0ab 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The API will be available at: ## Running the API locally via our AWS dev domain -This will make the local environment live at: https://[NAME].dev.rdc.library.northwestern.edu:3002/search +This will make the local environment live at: https://[DEV_PREFIX].dev.rdc.library.northwestern.edu:3002/search ``` docker run --rm -it -d \ From 03e6eae228ed33d055a9420bbedc1adc2eef4947 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Fri, 28 Oct 2022 19:10:35 +0000 Subject: [PATCH 49/61] Adds /auth/logout handler --- src/handlers/get-auth-logout.js | 39 ++++++++++++++++++++++++ template.yaml | 16 ++++++++++ test/integration/get-auth-logout.test.js | 29 ++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/handlers/get-auth-logout.js create mode 100644 test/integration/get-auth-logout.test.js diff --git a/src/handlers/get-auth-logout.js b/src/handlers/get-auth-logout.js new file mode 100644 index 00000000..5d23a2d3 --- /dev/null +++ b/src/handlers/get-auth-logout.js @@ -0,0 +1,39 @@ +const axios = require("axios").default; +const cookie = require("cookie"); +const { processRequest, processResponse } = require("./middleware"); + +/** + * Performs NUSSO logout + */ +exports.handler = async (event) => { + event = processRequest(event); + const url = `${process.env.NUSSO_BASE_URL}logout`; + let resp; + + await axios + .get(url, { headers: { apikey: process.env.NUSSO_API_KEY } }) + .then((response) => { + resp = { + statusCode: 302, + cookies: [ + cookie.serialize("dcApiV2Token", null, { + expires: new Date(1), + domain: "library.northwestern.edu", + path: "/", + secure: true, + }), + ], + headers: { + location: response.data.url, + }, + }; + }) + .catch((error) => { + console.error("NUSSO request error", error); + resp = { + statusCode: 401, + }; + }); + + return processResponse(event, resp); +}; diff --git a/template.yaml b/template.yaml index cbc325bf..105c06f3 100644 --- a/template.yaml +++ b/template.yaml @@ -97,6 +97,22 @@ Resources: ApiId: !Ref dcApi Path: /auth/login Method: GET + getAuthLogoutFunction: + Type: AWS::Serverless::Function + Properties: + Handler: handlers/get-auth-logout.handler + Description: Performs NUSSO logout. + Environment: + Variables: + NUSSO_API_KEY: !Ref NussoApiKey + NUSSO_BASE_URL: !Ref NussoBaseUrl + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /auth/logout + Method: GET getAuthWhoAmIFunction: Type: AWS::Serverless::Function Properties: diff --git a/test/integration/get-auth-logout.test.js b/test/integration/get-auth-logout.test.js new file mode 100644 index 00000000..5f4ac560 --- /dev/null +++ b/test/integration/get-auth-logout.test.js @@ -0,0 +1,29 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const nock = require("nock"); +const getAuthLogoutHandler = require("../../src/handlers/get-auth-logout"); + +describe("auth logout", function () { + helpers.saveEnvironment(); + + it("logs a user out of NU WebSSO and expires the dcApiV2Token", async () => { + process.env.NUSSO_BASE_URL = "https://nusso-base.com/"; + process.env.NUSSO_API_KEY = "abc123"; + + const url = "https://test.com/northwestern#logout"; + const _scope = nock(process.env.NUSSO_BASE_URL).get("/logout").reply(200, { + url: url, + }); + + const event = helpers.mockEvent("GET", "/auth/logout").render(); + + const result = await getAuthLogoutHandler.handler(event); + expect(result.statusCode).to.eq(302); + expect(result.headers.location).to.eq(url); + expect(result.cookies[0]).to.contain( + "Expires=Thu, 01 Jan 1970 00:00:00 GMT;" + ); + }); +}); From 6603eb899719ad16ed232f652858e548f2f6eb3e Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Mon, 31 Oct 2022 15:45:53 +0000 Subject: [PATCH 50/61] Fix shared links allow published logic --- src/api/opensearch.js | 19 ++++----- src/handlers/get-shared-link-by-id.js | 5 ++- .../mocks/private-unpublished-work-1234.json | 13 ++++++ .../integration/get-shared-link-by-id.test.js | 40 +++++++++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/mocks/private-unpublished-work-1234.json diff --git a/src/api/opensearch.js b/src/api/opensearch.js index 6bf3af3a..5aa7b6a5 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -18,12 +18,12 @@ async function getSharedLink(id, opts) { return getDocument("shared_links", id, opts); } -async function getDocument(index, id, opts) { +async function getDocument(index, id, opts = {}) { const request = initRequest(`/${prefix(index)}/_doc/${id}`); let response = await awsFetch(request); if (response.statusCode === 200) { const body = JSON.parse(response.body); - if (index != "shared_links" && !isVisible(body, opts?.allowPrivate)) { + if (index != "shared_links" && !isVisible(body, opts)) { let responseBody = { _index: prefix(index), _type: "_doc", @@ -40,14 +40,15 @@ async function getDocument(index, id, opts) { return response; } -function isVisible(doc, allowPrivate) { +function isVisible(doc, { allowPrivate, allowUnpublished }) { if (!doc?.found) return false; - const isAllowed = allowPrivate || doc?._source?.visibility !== "Private"; - if (doc?._source.api_model == "FileSet") { - return isAllowed; - } else { - return doc?._source?.published && isAllowed; - } + const isAllowedVisibility = + allowPrivate || doc?._source.visibility !== "Private"; + const isAllowedPublished = + allowUnpublished || + doc?._source.published || + doc?._source.api_model == "FileSet"; + return isAllowedVisibility && isAllowedPublished; } function initRequest(path) { diff --git a/src/handlers/get-shared-link-by-id.js b/src/handlers/get-shared-link-by-id.js index 9ac753e0..4d40475f 100644 --- a/src/handlers/get-shared-link-by-id.js +++ b/src/handlers/get-shared-link-by-id.js @@ -16,7 +16,10 @@ exports.handler = async (event) => { if (linkExpired(expirationDate) || !workId) return invalidRequest("Not Found"); - const workResponse = await getWork(workId, { allowPrivate: true }); + const workResponse = await getWork(workId, { + allowPrivate: true, + allowUnpublished: true, + }); if (workResponse.statusCode !== 200) return invalidRequest("Not Found"); const response = opensearchResponse.transform(workResponse); return processResponse(event, response); diff --git a/test/fixtures/mocks/private-unpublished-work-1234.json b/test/fixtures/mocks/private-unpublished-work-1234.json new file mode 100644 index 00000000..5909da2a --- /dev/null +++ b/test/fixtures/mocks/private-unpublished-work-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-work", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "Work", + "published": false, + "visibility": "Private" + } +} diff --git a/test/integration/get-shared-link-by-id.test.js b/test/integration/get-shared-link-by-id.test.js index eea77329..44f7210c 100644 --- a/test/integration/get-shared-link-by-id.test.js +++ b/test/integration/get-shared-link-by-id.test.js @@ -59,5 +59,45 @@ describe("Retrieve shared link by id", () => { const result = await handler(event); expect(result.statusCode).to.eq(404); }); + + it("retrieves an unpublished single shared link document", async () => { + mock + .get("/shared_links/_doc/1234") + .reply(200, helpers.testFixture("mocks/shared-link-1234.json")); + + mock + .get("/dc-v2-work/_doc/1234") + .reply( + 200, + helpers.testFixture("mocks/private-unpublished-work-1234.json") + ); + + const event = helpers + .mockEvent("GET", "/shared-links/{id}") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + const resultBody = JSON.parse(result.body); + expect(resultBody.data.api_model).to.eq("Work"); + expect(resultBody.data.visibility).to.eq("Private"); + }); + + it("returns a 404 when the link exists but the work doesn't", async () => { + mock + .get("/shared_links/_doc/1234") + .reply(200, helpers.testFixture("mocks/shared-link-1234.json")); + + mock + .get("/dc-v2-work/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-work-1234.json")); + + const event = helpers + .mockEvent("GET", "/shared-links/{id}") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); }); }); From a66219098adc2573034e744a5c88cc3875a4f941 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 31 Oct 2022 16:03:41 +0000 Subject: [PATCH 51/61] Workaround for NUSSO missing directory attribute issue --- src/handlers/get-auth-callback.js | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/handlers/get-auth-callback.js b/src/handlers/get-auth-callback.js index 2a05b429..63273eca 100644 --- a/src/handlers/get-auth-callback.js +++ b/src/handlers/get-auth-callback.js @@ -50,6 +50,40 @@ async function redeemSsoToken(event) { ); const user = response.data.results[0]; return user; + } catch (err) { + if ((err = ~/Reason: ResponseCode 404 is treated as error/)) { + return await redeemForNetIdOnly(event); + } + console.error(err); + return null; + } + } else { + console.warn("No NUSSO token found in request"); + return null; + } +} + +async function redeemForNetIdOnly(event) { + if (event.cookieObject.nusso) { + try { + const response = await axios.get( + `${process.env.NUSSO_BASE_URL}validateWebSSOToken`, + { + headers: { + apikey: process.env.NUSSO_API_KEY, + webssotoken: event.cookieObject.nusso, + }, + } + ); + const { netid } = response.data; + const user = { + uid: netid, + displayName: netid, + givenName: netid, + sn: "(NetID)", + mail: `${netid}@e.northwestern.edu`, + }; + return user; } catch (err) { console.error(err); return null; From 4b9b90664491a02159d36fd2c740d85d4929c25c Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 31 Oct 2022 20:12:31 +0000 Subject: [PATCH 52/61] Make stubbed user match shape of actual user --- src/handlers/get-auth-callback.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/handlers/get-auth-callback.js b/src/handlers/get-auth-callback.js index 63273eca..07341d70 100644 --- a/src/handlers/get-auth-callback.js +++ b/src/handlers/get-auth-callback.js @@ -78,9 +78,9 @@ async function redeemForNetIdOnly(event) { const { netid } = response.data; const user = { uid: netid, - displayName: netid, - givenName: netid, - sn: "(NetID)", + displayName: [netid], + givenName: [netid], + sn: ["(NetID)"], mail: `${netid}@e.northwestern.edu`, }; return user; From 03e4b4ff1f47254da65d7ede0406cdad41487f25 Mon Sep 17 00:00:00 2001 From: Brendan Quinn Date: Mon, 31 Oct 2022 21:22:41 +0000 Subject: [PATCH 53/61] Add support for the 'sort' query string parameter in GET searches Sort json in pipeline and event-builder, add docs for sort --- docs/docs/spec/openapi.yaml | 69 ++++++++++++++++-------------- docs/docs/spec/types.yaml | 15 +++++-- package-lock.json | 68 ++++++++++++++++++++++++++++- src/api/request/pipeline.js | 3 +- src/handlers/search-runner.js | 45 ++++++++++--------- src/package-lock.json | 65 +++++++++++++++++++++++++++- src/package.json | 3 +- test/integration/search.test.js | 25 +++++++++++ test/test-helpers/event-builder.js | 4 +- 9 files changed, 235 insertions(+), 62 deletions(-) diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index 2bd16144..dbb386ea 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -11,7 +11,7 @@ info: name: NUL Repository Team email: repository@northwestern.edu servers: -- url: https://dcapi.rdc.library.northwestern.edu/api/v2 + - url: https://dcapi.rdc.library.northwestern.edu/api/v2 tags: - name: Models description: > @@ -24,21 +24,22 @@ paths: get: operationId: getCollections tags: - - Collection + - Collection parameters: - - $ref: "./types.yaml#/components/parameters/page" - - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/sort" responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" /collections/{id}: get: tags: - - Collection + - Collection operationId: getCollectionById parameters: - - $ref: "./types.yaml#/components/parameters/id" - - $ref: "./types.yaml#/components/parameters/as" + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/as" responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" @@ -46,12 +47,12 @@ paths: get: operationId: getCollectionThumbnail tags: - - Collection - parameters: - - $ref: "./types.yaml#/components/parameters/id" - - $ref: "./types.yaml#/components/parameters/id" - - $ref: "./types.yaml#/components/parameters/thumbnailAspect" - - $ref: "./types.yaml#/components/parameters/thumbnailSize" + - Collection + parameters: + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/thumbnailAspect" + - $ref: "./types.yaml#/components/parameters/thumbnailSize" responses: 200: description: A thumbnail image for the given collection @@ -64,9 +65,9 @@ paths: get: operationId: getFileSetById tags: - - FileSet + - FileSet parameters: - - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/id" responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" @@ -74,9 +75,9 @@ paths: get: operationId: getWorkById tags: - - Work + - Work parameters: - - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/id" responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" @@ -84,12 +85,13 @@ paths: get: operationId: getSimilarWorks tags: - - Work + - Work parameters: - - $ref: "./types.yaml#/components/parameters/id" - - $ref: "./types.yaml#/components/parameters/page" - - $ref: "./types.yaml#/components/parameters/size" - - $ref: "./types.yaml#/components/parameters/as" + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/page" + - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/sort" + - $ref: "./types.yaml#/components/parameters/as" responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" @@ -97,11 +99,11 @@ paths: get: operationId: getWorkThumbnail tags: - - Work - parameters: - - $ref: "./types.yaml#/components/parameters/id" - - $ref: "./types.yaml#/components/parameters/thumbnailAspect" - - $ref: "./types.yaml#/components/parameters/thumbnailSize" + - Work + parameters: + - $ref: "./types.yaml#/components/parameters/id" + - $ref: "./types.yaml#/components/parameters/thumbnailAspect" + - $ref: "./types.yaml#/components/parameters/thumbnailSize" responses: 200: description: A thumbnail image for the given work @@ -114,12 +116,13 @@ paths: get: operationId: getSearch tags: - - Search + - Search parameters: - $ref: "./types.yaml#/components/parameters/query" - $ref: "./types.yaml#/components/parameters/searchToken" - $ref: "./types.yaml#/components/parameters/page" - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/sort" - $ref: "./types.yaml#/components/parameters/as" responses: 200: @@ -127,13 +130,13 @@ paths: post: operationId: postSearch tags: - - Search + - Search requestBody: content: application/json: schema: type: object - + responses: 200: $ref: "./types.yaml#/components/responses/SearchResponse" @@ -141,13 +144,14 @@ paths: get: operationId: getSearchWithModels tags: - - Search + - Search parameters: - $ref: "./types.yaml#/components/parameters/models" - $ref: "./types.yaml#/components/parameters/query" - $ref: "./types.yaml#/components/parameters/searchToken" - $ref: "./types.yaml#/components/parameters/page" - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/sort" - $ref: "./types.yaml#/components/parameters/as" responses: 200: @@ -155,11 +159,12 @@ paths: post: operationId: postSearchWithModels tags: - - Search + - Search parameters: - $ref: "./types.yaml#/components/parameters/models" - $ref: "./types.yaml#/components/parameters/page" - $ref: "./types.yaml#/components/parameters/size" + - $ref: "./types.yaml#/components/parameters/sort" - $ref: "./types.yaml#/components/parameters/as" responses: 200: diff --git a/docs/docs/spec/types.yaml b/docs/docs/spec/types.yaml index f7e967ef..ba9e2ad6 100644 --- a/docs/docs/spec/types.yaml +++ b/docs/docs/spec/types.yaml @@ -53,6 +53,13 @@ components: schema: type: integer minimum: 0 + sort: + name: sort + in: query + required: false + description: Comma-delimited list of fields to sort search results (e.g. "create_date:asc,modified_date:desc") + schema: + type: string as: name: as in: query @@ -70,10 +77,10 @@ components: description: Desired aspect ratio schema: type: string - enum: + enum: - full - square - thumbnailSize: + thumbnailSize: name: size in: query required: false @@ -100,8 +107,8 @@ components: application/json: schema: oneOf: - - $ref: "#/components/schemas/OpenSearchResponse" - - $ref: "#/components/schemas/IiifPresentationManifest" + - $ref: "#/components/schemas/OpenSearchResponse" + - $ref: "#/components/schemas/IiifPresentationManifest" schemas: IiifPresentationManifest: type: object diff --git a/package-lock.json b/package-lock.json index ca2c53ef..823c56f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1802,6 +1802,22 @@ "node": ">=0.4.0" } }, + "node_modules/detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/diff": { "version": "5.0.0", "dev": true, @@ -2706,6 +2722,14 @@ "node": ">=10" } }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", @@ -3426,6 +3450,19 @@ "dev": true, "license": "ISC" }, + "node_modules/sort-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.1.tgz", + "integrity": "sha512-s8cs2bcsQCzo/P2T/uoU6Js4dS/jnX8+4xunziNoq9qmSpZNCrRIAIvp4avsz0ST18HycV4z/7myJ7jsHWB2XQ==", + "dependencies": { + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "minimist": "^1.2.0" + }, + "bin": { + "sort-json": "app/cmd.js" + } + }, "node_modules/source-map": { "version": "0.6.1", "dev": true, @@ -3831,7 +3868,8 @@ "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", - "parse-http-header": "^1.0.1" + "parse-http-header": "^1.0.1", + "sort-json": "^2.0.1" } } }, @@ -5058,7 +5096,8 @@ "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", - "parse-http-header": "^1.0.1" + "parse-http-header": "^1.0.1", + "sort-json": "^2.0.1" } }, "debug": { @@ -5095,6 +5134,16 @@ "delayed-stream": { "version": "1.0.0" }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==" + }, "diff": { "version": "5.0.0", "dev": true @@ -5663,6 +5712,11 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, "mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", @@ -6129,6 +6183,16 @@ "version": "3.0.7", "dev": true }, + "sort-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.1.tgz", + "integrity": "sha512-s8cs2bcsQCzo/P2T/uoU6Js4dS/jnX8+4xunziNoq9qmSpZNCrRIAIvp4avsz0ST18HycV4z/7myJ7jsHWB2XQ==", + "requires": { + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "minimist": "^1.2.0" + } + }, "source-map": { "version": "0.6.1", "dev": true diff --git a/src/api/request/pipeline.js b/src/api/request/pipeline.js index 73525eb2..691b2c1e 100644 --- a/src/api/request/pipeline.js +++ b/src/api/request/pipeline.js @@ -1,4 +1,5 @@ const { isFromReadingRoom } = require("../../helpers"); +const sortJson = require("sort-json"); function filterFor(query, event) { const matchTheQuery = query; @@ -32,6 +33,6 @@ module.exports = class RequestPipeline { } toJson() { - return JSON.stringify(this.searchContext); + return JSON.stringify(sortJson(this.searchContext)); } }; diff --git a/src/handlers/search-runner.js b/src/handlers/search-runner.js index 7fe29242..5875528b 100644 --- a/src/handlers/search-runner.js +++ b/src/handlers/search-runner.js @@ -86,37 +86,42 @@ const constructSearchContext = async (event) => { searchContext.size = queryStringParameters.size || searchContext.size || 10; searchContext.from = queryStringParameters.from || searchContext.from || 0; + if (queryStringParameters?.sort || searchContext.sort) + searchContext.sort = + parseSortParameter(queryStringParameters) || searchContext.sort; + if (queryStringParameters.page) { const page = Number(queryStringParameters.page || 1); const size = Number(queryStringParameters.size || 10); searchContext.from = (page - 1) * size; } - // if (queryStringParameters.sort) { - // //TODO - // } - return searchContext; }; -const fromQueryString = ({ queryStringParameters, requestContext }) => { - if (queryStringParameters?.query) { - return { - query: { - query_string: { - query: queryStringParameters.query, - }, +const fromQueryString = ({ queryStringParameters, _requestContext }) => { + let request = { + query: { + query_string: { + query: queryStringParameters?.query || "*", }, - }; - } else { - return { - query: { - query_string: { - query: "*", - }, - }, - }; + }, + }; + return request; +}; + +const parseSortParameter = ({ sort: sortString }) => { + if (sortString == undefined) return null; + let values = []; + + for (const el of sortString.split(",")) { + let obj = {}; + const [key, value] = el.split(":"); + obj[key] = value; + values.push(obj); } + + return values; }; const responseFormat = async (event) => { diff --git a/src/package-lock.json b/src/package-lock.json index d630d22c..b82b4ab9 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -19,7 +19,8 @@ "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", - "parse-http-header": "^1.0.1" + "parse-http-header": "^1.0.1", + "sort-json": "^2.0.1" } }, "node_modules/@aws-crypto/ie11-detection": { @@ -1119,6 +1120,22 @@ "node": ">=0.4.0" } }, + "node_modules/detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -1304,6 +1321,14 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", @@ -1394,6 +1419,19 @@ "semver": "bin/semver" } }, + "node_modules/sort-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.1.tgz", + "integrity": "sha512-s8cs2bcsQCzo/P2T/uoU6Js4dS/jnX8+4xunziNoq9qmSpZNCrRIAIvp4avsz0ST18HycV4z/7myJ7jsHWB2XQ==", + "dependencies": { + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "minimist": "^1.2.0" + }, + "bin": { + "sort-json": "app/cmd.js" + } + }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -2473,6 +2511,16 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==" + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2612,6 +2660,11 @@ "mime-db": "1.52.0" } }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, "mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", @@ -2665,6 +2718,16 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, + "sort-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.1.tgz", + "integrity": "sha512-s8cs2bcsQCzo/P2T/uoU6Js4dS/jnX8+4xunziNoq9qmSpZNCrRIAIvp4avsz0ST18HycV4z/7myJ7jsHWB2XQ==", + "requires": { + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "minimist": "^1.2.0" + } + }, "tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", diff --git a/src/package.json b/src/package.json index 5e9b3b1c..055450ae 100644 --- a/src/package.json +++ b/src/package.json @@ -16,6 +16,7 @@ "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", "lz-string": "^1.4.4", - "parse-http-header": "^1.0.1" + "parse-http-header": "^1.0.1", + "sort-json": "^2.0.1" } } diff --git a/test/integration/search.test.js b/test/integration/search.test.js index b41b36f6..88c06016 100644 --- a/test/integration/search.test.js +++ b/test/integration/search.test.js @@ -174,5 +174,30 @@ describe("Search routes", () => { const resultBody = JSON.parse(result.body); expect(resultBody.type).to.eq("Collection"); }); + + it("allows sorting via query string parameters", async () => { + const originalQuery = { + query: { query_string: { query: "*" } }, + sort: [{ create_date: "asc" }, { modified_date: "desc" }], + }; + const authQuery = new RequestPipeline(originalQuery) + .authFilter() + .toJson(); + + mock + .post("/dc-v2-work/_search", authQuery) + .reply(200, helpers.testFixture("mocks/search.json")); + + const event = helpers + .mockEvent("GET", "/search") + .queryParams({ sort: "create_date:asc,modified_date:desc" }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(200); + const resultBody = JSON.parse(result.body); + expect(resultBody.pagination.query_url).to.contain( + "?sort=create_date%3Aasc%2Cmodified_date%3Adesc" + ); + }); }); }); diff --git a/test/test-helpers/event-builder.js b/test/test-helpers/event-builder.js index c9e30688..aa94f992 100644 --- a/test/test-helpers/event-builder.js +++ b/test/test-helpers/event-builder.js @@ -1,3 +1,5 @@ +const sortJson = require("sort-json"); + module.exports = class { constructor(method, route) { const now = new Date(); @@ -116,6 +118,6 @@ module.exports = class { ).toString(); } - return result; + return sortJson(result); } }; From a6a9da3ee81918934207db60319edb22c4894323 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Thu, 3 Nov 2022 18:52:17 +0000 Subject: [PATCH 54/61] Don't return unpublished file sets --- src/api/opensearch.js | 5 +---- test/fixtures/mocks/fileset-1234.json | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/api/opensearch.js b/src/api/opensearch.js index 5aa7b6a5..22ea4cc7 100644 --- a/src/api/opensearch.js +++ b/src/api/opensearch.js @@ -44,10 +44,7 @@ function isVisible(doc, { allowPrivate, allowUnpublished }) { if (!doc?.found) return false; const isAllowedVisibility = allowPrivate || doc?._source.visibility !== "Private"; - const isAllowedPublished = - allowUnpublished || - doc?._source.published || - doc?._source.api_model == "FileSet"; + const isAllowedPublished = allowUnpublished || doc?._source.published; return isAllowedVisibility && isAllowedPublished; } diff --git a/test/fixtures/mocks/fileset-1234.json b/test/fixtures/mocks/fileset-1234.json index e0b5bc13..0c8eb41d 100644 --- a/test/fixtures/mocks/fileset-1234.json +++ b/test/fixtures/mocks/fileset-1234.json @@ -6,6 +6,8 @@ "found": true, "_source": { "id": "1234", - "api_model": "FileSet" + "api_model": "FileSet", + "visibility": "Public", + "published": true } } From 1bb3d24e717d32a915c8348b6f9fdafa56cf8ff4 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Fri, 28 Oct 2022 18:19:12 +0000 Subject: [PATCH 55/61] Add file set authorization routes --- docs/docs/spec/openapi.yaml | 12 ++ package-lock.json | 12 ++ package.json | 2 +- src/handlers/get-file-set-auth.js | 64 +++++++++ src/package-lock.json | 11 ++ src/package.json | 1 + template.yaml | 23 +++ test/fixtures/mocks/fileset-baddata-1234.json | 13 ++ test/fixtures/mocks/fileset-netid-1234.json | 13 ++ .../mocks/fileset-restricted-1234.json | 13 ++ .../fileset-restricted-unpublished-1234.json | 13 ++ .../mocks/fileset-unpublished-1234.json | 13 ++ test/fixtures/mocks/missing-fileset-1234.json | 6 + test/integration/get-file-set-auth.test.js | 133 ++++++++++++++++++ ...t-work-by-id.js => get-work-by-id.test.js} | 0 15 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 src/handlers/get-file-set-auth.js create mode 100644 test/fixtures/mocks/fileset-baddata-1234.json create mode 100644 test/fixtures/mocks/fileset-netid-1234.json create mode 100644 test/fixtures/mocks/fileset-restricted-1234.json create mode 100644 test/fixtures/mocks/fileset-restricted-unpublished-1234.json create mode 100644 test/fixtures/mocks/fileset-unpublished-1234.json create mode 100644 test/fixtures/mocks/missing-fileset-1234.json create mode 100644 test/integration/get-file-set-auth.test.js rename test/integration/{get-work-by-id.js => get-work-by-id.test.js} (100%) diff --git a/docs/docs/spec/openapi.yaml b/docs/docs/spec/openapi.yaml index dbb386ea..aec9aaa0 100644 --- a/docs/docs/spec/openapi.yaml +++ b/docs/docs/spec/openapi.yaml @@ -71,6 +71,18 @@ paths: responses: 200: $ref: "./types.yaml#/components/responses/DocumentResponse" + /file-sets/{id}/authorization: + get: + operationId: getFileSetAuth + tags: + - FileSet + parameters: + - $ref: "./types.yaml#/components/parameters/id" + responses: + 204: + description: "The resource is authorized" + 403: + $ref: "The resource is not authorized" /works/{id}: get: operationId: getWorkById diff --git a/package-lock.json b/package-lock.json index 823c56f0..f1ab05ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2613,6 +2613,11 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -3867,6 +3872,7 @@ "cookie": "^0.5.0", "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", + "lodash.isobject": "^3.0.2", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1", "sort-json": "^2.0.1" @@ -5095,6 +5101,7 @@ "cookie": "^0.5.0", "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", + "lodash.isobject": "^3.0.2", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1", "sort-json": "^2.0.1" @@ -5642,6 +5649,11 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", diff --git a/package.json b/package.json index c1cc054b..8e37829d 100644 --- a/package.json +++ b/package.json @@ -23,4 +23,4 @@ "nyc": "^15.1.0", "prettier": "^2.7.1" } -} +} \ No newline at end of file diff --git a/src/handlers/get-file-set-auth.js b/src/handlers/get-file-set-auth.js new file mode 100644 index 00000000..830986eb --- /dev/null +++ b/src/handlers/get-file-set-auth.js @@ -0,0 +1,64 @@ +const { processRequest, processResponse } = require("./middleware"); +const { getFileSet } = require("../api/opensearch"); +const { isFromReadingRoom } = require("../helpers"); +const isObject = require("lodash.isobject"); +const jwt = require("jsonwebtoken"); + +/** + * Authorizes a FileSet by id + */ +exports.handler = async (event) => { + event = processRequest(event); + const id = event.pathParameters.id; + const osResponse = await getFileSet(id, { + allowPrivate: true, + allowUnpublished: true, + }); + + if (osResponse.statusCode != 200) { + return sendResponse(event, osResponse.statusCode, osResponse.statusCode); + } + + const body = JSON.parse(osResponse.body); + + if (!body?.found) { + return sendResponse(event, 404); + } + + const fileSet = JSON.parse(osResponse.body)._source; + const token = event.cookieObject.dcApiV2Token; + const visibility = fileSet.visibility; + const published = fileSet.published; + const readingRoom = isFromReadingRoom(event); + + if (isAllowedVisibility(token, visibility, readingRoom) && published) { + return sendResponse(event, 204); + } else { + return sendResponse(event, 403); + } +}; + +function sendResponse(event, statusCode) { + return processResponse(event, { + statusCode: statusCode, + }); +} + +function isAllowedVisibility(token, visibility, readingRoom) { + switch (visibility) { + case "Public": + return true; + case "Institution": + return isValidToken(token); + case "Private": + return readingRoom; + default: + return false; + } +} + +function isValidToken(token) { + if (!!token === false) return false; + const user = jwt.verify(token, process.env.API_TOKEN_SECRET); + return isObject(user); +} diff --git a/src/package-lock.json b/src/package-lock.json index b82b4ab9..1015f252 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -18,6 +18,7 @@ "cookie": "^0.5.0", "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", + "lodash.isobject": "^3.0.2", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1", "sort-json": "^2.0.1" @@ -1279,6 +1280,11 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -2627,6 +2633,11 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", diff --git a/src/package.json b/src/package.json index 055450ae..cf745ba1 100644 --- a/src/package.json +++ b/src/package.json @@ -15,6 +15,7 @@ "cookie": "^0.5.0", "iiif-builder": "^1.0.6", "jsonwebtoken": "^8.5.1", + "lodash.isobject": "^3.0.2", "lz-string": "^1.4.4", "parse-http-header": "^1.0.1", "sort-json": "^2.0.1" diff --git a/template.yaml b/template.yaml index 105c06f3..60a55d09 100644 --- a/template.yaml +++ b/template.yaml @@ -190,6 +190,29 @@ Resources: ApiId: !Ref dcApi Path: /file-sets/{id} Method: GET + getFileSetAuthFunction: + Type: AWS::Serverless::Function + Properties: + Handler: handlers/get-file-set-auth.handler + Description: Authorizes access to a file set. + Policies: + Version: 2012-10-17 + Statement: + - Sid: ESHTTPPolicy + Effect: Allow + Action: + - es:ESHttp* + Resource: "*" + Environment: + Variables: + API_TOKEN_SECRET: !Ref ApiSecret + Events: + Api: + Type: HttpApi + Properties: + ApiId: !Ref dcApi + Path: /file-sets/{id}/authorization + Method: GET getWorkByIdFunction: Type: AWS::Serverless::Function Properties: diff --git a/test/fixtures/mocks/fileset-baddata-1234.json b/test/fixtures/mocks/fileset-baddata-1234.json new file mode 100644 index 00000000..7512aac1 --- /dev/null +++ b/test/fixtures/mocks/fileset-baddata-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet", + "visibility": "Restricted", + "published": true + } +} diff --git a/test/fixtures/mocks/fileset-netid-1234.json b/test/fixtures/mocks/fileset-netid-1234.json new file mode 100644 index 00000000..59aee72b --- /dev/null +++ b/test/fixtures/mocks/fileset-netid-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet", + "visibility": "Institution", + "published": true + } +} diff --git a/test/fixtures/mocks/fileset-restricted-1234.json b/test/fixtures/mocks/fileset-restricted-1234.json new file mode 100644 index 00000000..017c32ab --- /dev/null +++ b/test/fixtures/mocks/fileset-restricted-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet", + "visibility": "Private", + "published": true + } +} diff --git a/test/fixtures/mocks/fileset-restricted-unpublished-1234.json b/test/fixtures/mocks/fileset-restricted-unpublished-1234.json new file mode 100644 index 00000000..5d559c47 --- /dev/null +++ b/test/fixtures/mocks/fileset-restricted-unpublished-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet", + "visibility": "Private", + "published": false + } +} diff --git a/test/fixtures/mocks/fileset-unpublished-1234.json b/test/fixtures/mocks/fileset-unpublished-1234.json new file mode 100644 index 00000000..50ee994b --- /dev/null +++ b/test/fixtures/mocks/fileset-unpublished-1234.json @@ -0,0 +1,13 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "_version": 1, + "found": true, + "_source": { + "id": "1234", + "api_model": "FileSet", + "visibility": "Public", + "published": false + } +} diff --git a/test/fixtures/mocks/missing-fileset-1234.json b/test/fixtures/mocks/missing-fileset-1234.json new file mode 100644 index 00000000..4db6424d --- /dev/null +++ b/test/fixtures/mocks/missing-fileset-1234.json @@ -0,0 +1,6 @@ +{ + "_index": "dev-dc-v2-file-set", + "_type": "_doc", + "_id": "1234", + "found": false +} diff --git a/test/integration/get-file-set-auth.test.js b/test/integration/get-file-set-auth.test.js new file mode 100644 index 00000000..1cf0d91d --- /dev/null +++ b/test/integration/get-file-set-auth.test.js @@ -0,0 +1,133 @@ +"use strict"; + +const chai = require("chai"); +const expect = chai.expect; +const RequestPipeline = require("../../src/api/request/pipeline"); +chai.use(require("chai-http")); + +process.env.API_TOKEN_SECRET = "abc123"; + +describe("Authorize a file set by id", () => { + helpers.saveEnvironment(); + const mock = helpers.mockIndex(); + + describe("GET /file-sets/{id}/authorization", () => { + const { handler } = require("../../src/handlers/get-file-set-auth"); + + it("authorizes a public, published file set with no token", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); + + it("does not authorize a public, unpublished file set even with a valid token", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-unpublished-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .headers({ + Cookie: + "dcApiV2Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaXNwbGF5TmFtZSI6IlNvbWUgT25lIiwiaWF0IjoxNjY1NDE3NzYzfQ.Nwi8dJnc7w201ZtO5de5zYmU-F5gEalkmHZ5pR1VXms;", + }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(403); + }); + + it("authorizes a netid, published file set with a valid token", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-netid-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .headers({ + Cookie: + "dcApiV2Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaXNwbGF5TmFtZSI6IlNvbWUgT25lIiwiaWF0IjoxNjY1NDE3NzYzfQ.Nwi8dJnc7w201ZtO5de5zYmU-F5gEalkmHZ5pR1VXms;", + }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); + + it("does not authorize a netid, published file set with no token", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-netid-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(403); + }); + + it("authorizes an restricted file set if the user is in a Reading Room", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-restricted-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + process.env.READING_ROOM_IPS = event.requestContext.http.sourceIp; + + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); + + it("does not authorize a restricted, unpublished file set if the user is in a Reading Room", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-restricted-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + process.env.READING_ROOM_IPS = event.requestContext.http.sourceIp; + + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); + + it("404s a missing file set", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/missing-fileset-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(404); + }); + + it("does not authorize a file set with invalid visibility value", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-baddata-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(403); + }); + }); +}); diff --git a/test/integration/get-work-by-id.js b/test/integration/get-work-by-id.test.js similarity index 100% rename from test/integration/get-work-by-id.js rename to test/integration/get-work-by-id.test.js From f137027357766537a370498dab45151187d9f8e7 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 8 Nov 2022 22:29:04 +0000 Subject: [PATCH 56/61] =?UTF-8?q?Authorize=20fake=20=E2=80=9CFileSet?= =?UTF-8?q?=E2=80=9D=20requests=20when=20id=20matches=2000000000-0000-0000?= =?UTF-8?q?-0000-000000000xxx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/handlers/get-file-set-auth.js | 16 ++++++++++------ test/integration/get-file-set-auth.test.js | 13 +++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/handlers/get-file-set-auth.js b/src/handlers/get-file-set-auth.js index 830986eb..41e50edc 100644 --- a/src/handlers/get-file-set-auth.js +++ b/src/handlers/get-file-set-auth.js @@ -4,12 +4,21 @@ const { isFromReadingRoom } = require("../helpers"); const isObject = require("lodash.isobject"); const jwt = require("jsonwebtoken"); +const OPEN_DOCUMENT_NAMESPACE = /^0{8}-0{4}-0{4}-0{4}-0{9}[0-9A-Fa-f]{3}/; + /** * Authorizes a FileSet by id */ exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; + + console.log(id); + console.log(OPEN_DOCUMENT_NAMESPACE.test(id)); + // Special namespace for entities that aren't actual entities + // with indexed metadata (i.e., placeholder images) + if (OPEN_DOCUMENT_NAMESPACE.test(id)) return sendResponse(event, 204); + const osResponse = await getFileSet(id, { allowPrivate: true, allowUnpublished: true, @@ -20,12 +29,7 @@ exports.handler = async (event) => { } const body = JSON.parse(osResponse.body); - - if (!body?.found) { - return sendResponse(event, 404); - } - - const fileSet = JSON.parse(osResponse.body)._source; + const fileSet = body._source; const token = event.cookieObject.dcApiV2Token; const visibility = fileSet.visibility; const published = fileSet.published; diff --git a/test/integration/get-file-set-auth.test.js b/test/integration/get-file-set-auth.test.js index 1cf0d91d..9d75b5e1 100644 --- a/test/integration/get-file-set-auth.test.js +++ b/test/integration/get-file-set-auth.test.js @@ -129,5 +129,18 @@ describe("Authorize a file set by id", () => { const result = await handler(event); expect(result.statusCode).to.eq(403); }); + + it("authorizes requests for IDs in the always-allow namespace", async () => { + const id = "00000000-0000-0000-0000-000000000001"; + + mock.get(`/dc-v2-file-set/_doc/${id}`).reply(404, "Not Found"); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id }) + .render(); + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); }); }); From 5c1471af4947035c630633f879e11fbdf162b907 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Wed, 9 Nov 2022 15:53:48 +0000 Subject: [PATCH 57/61] Make sure both types of token (directory / stub) include netid --- src/handlers/get-auth-callback.js | 70 ++++++++++------------ src/handlers/get-file-set-auth.js | 2 - test/integration/get-auth-callback.test.js | 58 +++++++++++++++--- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/src/handlers/get-auth-callback.js b/src/handlers/get-auth-callback.js index 07341d70..492ec65a 100644 --- a/src/handlers/get-auth-callback.js +++ b/src/handlers/get-auth-callback.js @@ -3,6 +3,9 @@ const cookie = require("cookie"); const jwt = require("jsonwebtoken"); const { processRequest, processResponse } = require("./middleware"); +const BAD_DIRECTORY_SEARCH_FAULT = + /Reason: ResponseCode 404 is treated as error/; + /** * NUSSO auth callback */ @@ -36,60 +39,53 @@ exports.handler = async (event) => { return processResponse(event, response); }; +async function getNetIdFromToken(nusso) { + const response = await axios.get( + `${process.env.NUSSO_BASE_URL}validateWebSSOToken`, + { + headers: { + apikey: process.env.NUSSO_API_KEY, + webssotoken: nusso, + }, + } + ); + return response?.data?.netid; +} + async function redeemSsoToken(event) { - if (event.cookieObject.nusso) { + const nusso = event.cookieObject.nusso; + const netid = await getNetIdFromToken(nusso); + if (netid) { try { const response = await axios.get( `${process.env.NUSSO_BASE_URL}validate-with-directory-search-response`, { headers: { apikey: process.env.NUSSO_API_KEY, - webssotoken: event.cookieObject.nusso, + webssotoken: nusso, }, } ); - const user = response.data.results[0]; - return user; + return { ...response.data.results[0], uid: netid }; } catch (err) { - if ((err = ~/Reason: ResponseCode 404 is treated as error/)) { - return await redeemForNetIdOnly(event); + if ( + BAD_DIRECTORY_SEARCH_FAULT.test(err?.response?.data?.fault?.faultstring) + ) { + return redeemForNetIdOnly(netid); } - console.error(err); + console.error(err.response.data); return null; } } else { - console.warn("No NUSSO token found in request"); + console.warn("NUSSO token could not be redeemed"); return null; } } -async function redeemForNetIdOnly(event) { - if (event.cookieObject.nusso) { - try { - const response = await axios.get( - `${process.env.NUSSO_BASE_URL}validateWebSSOToken`, - { - headers: { - apikey: process.env.NUSSO_API_KEY, - webssotoken: event.cookieObject.nusso, - }, - } - ); - const { netid } = response.data; - const user = { - uid: netid, - displayName: [netid], - givenName: [netid], - sn: ["(NetID)"], - mail: `${netid}@e.northwestern.edu`, - }; - return user; - } catch (err) { - console.error(err); - return null; - } - } else { - console.warn("No NUSSO token found in request"); - return null; - } +function redeemForNetIdOnly(netid) { + return { + uid: netid, + displayName: [netid], + mail: `${netid}@e.northwestern.edu`, + }; } diff --git a/src/handlers/get-file-set-auth.js b/src/handlers/get-file-set-auth.js index 41e50edc..ab61cdec 100644 --- a/src/handlers/get-file-set-auth.js +++ b/src/handlers/get-file-set-auth.js @@ -13,8 +13,6 @@ exports.handler = async (event) => { event = processRequest(event); const id = event.pathParameters.id; - console.log(id); - console.log(OPEN_DOCUMENT_NAMESPACE.test(id)); // Special namespace for entities that aren't actual entities // with indexed metadata (i.e., placeholder images) if (OPEN_DOCUMENT_NAMESPACE.test(id)) return sendResponse(event, 204); diff --git a/test/integration/get-auth-callback.test.js b/test/integration/get-auth-callback.test.js index 0a864137..1cdf8b54 100644 --- a/test/integration/get-auth-callback.test.js +++ b/test/integration/get-auth-callback.test.js @@ -1,33 +1,75 @@ "use strict"; const chai = require("chai"); +const cookie = require("cookie"); const expect = chai.expect; +const jwt = require("jsonwebtoken"); const nock = require("nock"); const getAuthCallbackHandler = require("../../src/handlers/get-auth-callback"); describe("auth callback", function () { helpers.saveEnvironment(); - it("redeems the NUSSO token", async () => { + let event; + beforeEach(() => { process.env.NUSSO_BASE_URL = "https://nusso-base.com/"; process.env.NUSSO_API_KEY = "abc123"; process.env.API_TOKEN_SECRET = "abc123"; - const _scope = nock(process.env.NUSSO_BASE_URL) - .get("/validate-with-directory-search-response") - .reply(200, { - results: [{ displayName: "Some User" }], - }); - - const event = helpers + event = helpers .mockEvent("GET", "/auth/callback") .headers({ Cookie: "nusso=bnVzc28=;redirectUrl=aHR0cHM6Ly9leGFtcGxlLmNvbQ==;", }) .render(); + nock(process.env.NUSSO_BASE_URL) + .get("/validateWebSSOToken") + .reply(200, { netid: "uid123" }); + }); + + it("redeems the NUSSO token", async () => { + nock(process.env.NUSSO_BASE_URL) + .get("/validate-with-directory-search-response") + .reply(200, { + results: [{ displayName: ["Some User"] }], + }); + const result = await getAuthCallbackHandler.handler(event); + + expect(result.statusCode).to.eq(302); + expect(result.headers.location).to.eq("https://example.com"); + + const { dcApiV2Token } = cookie.parse(result.cookies[0]); + const token = jwt.verify(dcApiV2Token, process.env.API_TOKEN_SECRET); + expect(token).to.deep.include({ + displayName: ["Some User"], + uid: "uid123", + }); + }); + + it("assembles a user object from the netID if directory search fails", async () => { + nock(process.env.NUSSO_BASE_URL) + .get("/validate-with-directory-search-response") + .reply(500, { + fault: { + faultstring: + "Execution of ServiceCallout Call-WebSSO-API failed. Reason: ResponseCode 404 is treated as error", + detail: { errorcode: "steps.servicecallout.ExecutionFailed" }, + }, + }); + + const result = await getAuthCallbackHandler.handler(event); + expect(result.statusCode).to.eq(302); expect(result.headers.location).to.eq("https://example.com"); + + const { dcApiV2Token } = cookie.parse(result.cookies[0]); + const token = jwt.verify(dcApiV2Token, process.env.API_TOKEN_SECRET); + expect(token).to.deep.include({ + displayName: ["uid123"], + mail: "uid123@e.northwestern.edu", + uid: "uid123", + }); }); }); From 874c626113a39db8133264917449d9e39a2d8955 Mon Sep 17 00:00:00 2001 From: Karen Shaw Date: Fri, 11 Nov 2022 17:09:49 +0000 Subject: [PATCH 58/61] Net ID file set authorization for reading room --- src/handlers/get-file-set-auth.js | 2 +- test/integration/get-file-set-auth.test.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/handlers/get-file-set-auth.js b/src/handlers/get-file-set-auth.js index ab61cdec..e14a6aab 100644 --- a/src/handlers/get-file-set-auth.js +++ b/src/handlers/get-file-set-auth.js @@ -51,7 +51,7 @@ function isAllowedVisibility(token, visibility, readingRoom) { case "Public": return true; case "Institution": - return isValidToken(token); + return isValidToken(token) || readingRoom; case "Private": return readingRoom; default: diff --git a/test/integration/get-file-set-auth.test.js b/test/integration/get-file-set-auth.test.js index 9d75b5e1..7620e5e3 100644 --- a/test/integration/get-file-set-auth.test.js +++ b/test/integration/get-file-set-auth.test.js @@ -74,6 +74,21 @@ describe("Authorize a file set by id", () => { expect(result.statusCode).to.eq(403); }); + it("does authorize a netid, published file set with no token if the user is in the reading room", async () => { + mock + .get("/dc-v2-file-set/_doc/1234") + .reply(200, helpers.testFixture("mocks/fileset-netid-1234.json")); + + const event = helpers + .mockEvent("GET", "/file-sets/{id}/authorization") + .pathParams({ id: 1234 }) + .render(); + + process.env.READING_ROOM_IPS = event.requestContext.http.sourceIp; + const result = await handler(event); + expect(result.statusCode).to.eq(204); + }); + it("authorizes an restricted file set if the user is in a Reading Room", async () => { mock .get("/dc-v2-file-set/_doc/1234") From acb55169917634271ea56786e96ac06a10a6a6b6 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Mon, 28 Nov 2022 19:47:20 +0000 Subject: [PATCH 59/61] Prefer the search context's size over the default --- src/handlers/search-runner.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/handlers/search-runner.js b/src/handlers/search-runner.js index 5875528b..2434cfc8 100644 --- a/src/handlers/search-runner.js +++ b/src/handlers/search-runner.js @@ -92,8 +92,7 @@ const constructSearchContext = async (event) => { if (queryStringParameters.page) { const page = Number(queryStringParameters.page || 1); - const size = Number(queryStringParameters.size || 10); - searchContext.from = (page - 1) * size; + searchContext.from = (page - 1) * searchContext.size; } return searchContext; From 50ea75d036aa0a758987566b4c595113e323de98 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 29 Nov 2022 12:43:54 -0600 Subject: [PATCH 60/61] Update HTTPS documentation --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e038d0ab..781adfb4 100644 --- a/README.md +++ b/README.md @@ -63,15 +63,8 @@ The API will be available at: ## Running the API locally via our AWS dev domain -This will make the local environment live at: https://[DEV_PREFIX].dev.rdc.library.northwestern.edu:3002/search +Use the [https-proxy](https://github.com/nulib/aws-developer-environment#convenience-scripts) script to make the local environment live at: https://[DEV_PREFIX].dev.rdc.library.northwestern.edu:3002/search ``` -docker run --rm -it -d \ - -e "UPSTREAM_DOMAIN=172.17.0.1" \ - -e "UPSTREAM_PORT=3000" \ - -e "PROXY_DOMAIN=$DEV_PREFIX.dev.rdc.library.northwestern.edu" \ - -v /home/ec2-user/.dev_cert/dev.rdc.cert.pem:/etc/nginx/certs/cert.pem \ - -v /home/ec2-user/.dev_cert/dev.rdc.key.pem:/etc/nginx/certs/key.pem \ - -p 3002:443 \ - outrigger/https-proxy:1.0 +https-proxy start 3002 3000 ``` From e0374cefd2c1b3becc1292d8c109b68e56b8d999 Mon Sep 17 00:00:00 2001 From: "Michael B. Klein" Date: Tue, 29 Nov 2022 21:47:59 +0000 Subject: [PATCH 61/61] API mappings need to depend on API --- template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/template.yaml b/template.yaml index 60a55d09..5e16f219 100644 --- a/template.yaml +++ b/template.yaml @@ -389,6 +389,7 @@ Resources: DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" ApiId: !Ref V1ApiId Stage: !Ref V1ApiStage + DependsOn: dcApi # root API rootApi: @@ -420,6 +421,7 @@ Resources: DomainName: !Sub "${CustomDomainHost}.${CustomDomainZone}" ApiId: !Ref rootApi Stage: !Ref rootApilatestStage + DependsOn: dcApi docsBucket: Type: AWS::S3::Bucket Properties: