From 469c8e799f796ad095ef2ac190709a2a320ec188 Mon Sep 17 00:00:00 2001 From: Kevin He Date: Mon, 13 Feb 2023 14:08:39 -0800 Subject: [PATCH] Keep feature branch `integrate-commerce-sdk-react` up to date (#960) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New year, new look! (#876) * New year, new look! * Use JS to compute current year. * Small bump to max deps allowed. Needed to so that non-code changes will pass CI. Hitting the limit can be addressed later. * Update from 2.5.0 (#881) * Starting release process for 2.5.0 * allow commerce react sdk to release * bump version to 2.5.0 (#879) * bump version to 2.5.0 * bump max packages * Begin development on 2.6.0 * Set commerce-sdk-react package to private (#882) * test-commerce: comment out not-implemented customer hooks (#877) * Change padding (#899) * [Snyk] Security upgrade eslint-import-resolver-webpack from 0.10.0 to 0.13.1 (#887) * fix: packages/internal-lib-build/package.json & packages/internal-lib-build/package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-DEBUG-3227433 * upgrade eslint-webpack-plugin-import, regen lock file Co-authored-by: Alex Vuong Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> * Cache SLAS callback using request processor (#884) * cache callback in request processor * fix import path * cache callback for a year * use native URLSearchParams * revert use native URLSearchParams * Rotate fingerprint ssh for deploy commerce sdk doc (#905) * Feature/megamenu fixes (#875) * [W-11996527] WIP - commit megamenu spike results / findings to feature branch * resolve search error * move default variables to constants file * cleanup code * add aria-live to menu categories * lint * implement aria live and busy * fix failing tests * update mock categories data, implement aria busy for listmenu * update mock data and fix list-menu test * lint * resolve unmounted component error * temporary testing server errors fix * set spinner opacity to 0 and increase max package value * lint * remove mock data from app code, clean up * allow padding of categories with loaded: true to bypass initial useEffect API call * fix last failing test * Update list-menu/index.jsx Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> * fix aria-busy implementation and cleanup * add more test cases - when there is no root categories * remove mit license comment * add depndency array to useeffect * globally mock getcategory api call * WIP * fix tests WIP * remove all instances of setupmockserver in test files * globally mock istokenvalid AND many other cleanup items * refactor tree walking code * add locale checks and time to localstorage * WIP removing tree walking logic * remove tree walking code, update setroot and extract stale time * add catch to promise all * lint * namespace constants, remove unnecessary try catch... * final cleanup * fix lint Co-authored-by: Brian Feister Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> Co-authored-by: Kevin He * Mega menu fixes (#910) * Remove unnecessary catch clause. * Update copyright. * Don't return error when category is expected. * Fix bug preventing cache invalidation. * Set fetchTime when data is fetched, rather than every page load. * Update comment. * Convert promise chain to async/await for readability. * Guard against missing category items. * Move comment inside code block. * Change useEffect back to promise chain, not async/await. useEffect expects the callback to return nothing or a function; returning a promise could break things. * GitHub Actions (#854) * Add test workflow * Use actions/checkout v3 * Don't use CI image * Add step install npm dependencies * Add setup action * Set DEVELOP and RELEASE env variables * Add required shell property * Update action.yml * Update action.yml * Adding test matrix * cleanup * Split setup windows and ubuntu * Update test.yml * Update test.yml * Add Lighthouse and smoketest scripts * Not using actions/cache for now * Add generated matrix * Add missing runs-on prop to generated matrix * Add Setup Node step to generated matrix * Add Setup machine to generated matrix * Add cron schedule * Add timeout-minutes 5 * Move timeout to generate project step * Add Run tests on generated projects * Use dynamic generated-project folder * Run tests on test-project and retail-react-app-demo * Add Push Bundle step * Skip flaky test * Disable fail-fast strategy * Use env variables * Re-arrange env * Add step before push bundle * cleanup * cleanup * Use temp test-env-3 * testing slack notifs * testing * add publish to npm step * fix indent * python-dev does not exist anymore * use python2 * increase max packages * test slack notifs * add snyk cli and datadog step * update mrt user credentials * testing slack with pwa kit channel * syntax * fix conditionals * test push bundle * add push bundle step for generated * syntax * fix syntax error * update slack payload * run steps in container * testing * refactor * syntax * sudo container error * testing * update * add pip * use different docker * no container * container * testing * add user to container * fix * syntax add shell * syntax errors * remove container, use act * syntax errors * add snyk audit and other syntax stuff * extract steps to own actions * add inputs for actions * add shell for steps in actions with run * project cannot be generated in action file * updated snyk token, uncommenting code * Fix typo. * Add missing appkey property. * Use snake_case names for legibility. * Restore missing clean check * Fix skipped conversion to snake_case. * Trim trailing whitespace. * Extract conditionals to vars and clean up vars. * Change env IS_TESTABLE_TEMPLATE to more clear IS_TEMPLATE_FROM_RETAIL_REACT_APP * Fix YAML breaking conditional. * Try explicitly checking value. * Try explicitly checking true/false string values. * Try string comparisons. * Fix bad YAML. * Replace " with ' * Get ready for the prime time! * Fail fast! * Update TODOs. * Clean up npm version management. * Add TODO to merge workflows. * Update step names. * End files with newline. * Run on pull_request to support forked repos. * Only run on push for develop/release. We can assume all other branches will eventually have a PR. * Only push to MRT when actually desired. * Get that JavaScript nonsense outta here! * Check DEVELOP in step conditional, not in action execution. * Add some TODOs. * Too many newlines! Co-authored-by: yunakim714 Co-authored-by: yunakim714 <84923642+yunakim714@users.noreply.github.com> Co-authored-by: Will Harney Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> * GitHub Actions fixes (#915) * Update is not fork check * Add single quote to cron expression * Add cron docs * Remove Circle CI config, fix IS_NOT_FORK (#921) * Fix develop branch name (#923) * Fix wrong proptypes (#924) * fix wrong proptypes * Update packages/template-retail-react-app/app/components/recommended-products/index.jsx Co-authored-by: Vincent Marta Co-authored-by: Brian Feister <47546998+bfeister@users.noreply.github.com> Co-authored-by: Vincent Marta * Update createOrder to send SLAS USID (#920) * Update createOrder to send SLAS USID * Modify some tests to include setting the header Co-authored-by: echessman <37908171+echessman@users.noreply.github.com> * Upgrade prettier to v2 (#926) * Upgrade to prettier v2 for modern TypeScript support. * Change trailing comma to prettier v1 default. Minimizes changes during v2 upgrade. * Apply prettier v2 formatting changes. Yaaay touching all of the files! * Set end of line to LF. * Remove unnecessary map statement (#929) * fix: packages/pwa-kit-runtime/package.json & packages/pwa-kit-runtime/package-lock.json to reduce vulnerabilities (#935) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-UAPARSERJS-3244450 Co-authored-by: snyk-bot * Update `develop` with v2.6.0 (#939) * Starting release process for 2.6.0 * 🚢 Release v2.6.0 (#937) * Update changelog files * Set `commerce-sdk-react-preview` as public * Version 2.6.0 * Begin development on 2.7.0 * Clean changelog files * Allow support for multiple sites concurrently w/ `commerce-sdk-react` (#911) * Namespace storage keys using the current siteId. Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> * @W-11920099 Re-write 'npm push' in Typescript, warn if Node deprecated (#763) * Rewrite ancient scripts in Typescript. * Show warnings on push when using a deprecated Node version. * Automatically select a `.mobify` credentials file based on `--cloud-origin`. * Fix broken CI – confusing path to package.json in the 'dist' directory! (#946) * Reliably look up project and pwa-kit-dev package.json in scripts * fix: handle special characters in `boldString` util (#942) + Add a new util for escaping special regex characters + Apply new util to `boldString` + Add and update util tests Co-authored-by: Brian Feister <47546998+bfeister@users.noreply.github.com> * Replace isomorphic jest mocks with msw handlers (#944) * replace most manual mocks * more replacements * more replacements in addresses test * lint * resolve flaky cart test * remove some jest mocks * replace all isomorphic mocks * cleanup * fix auth modal mocks * remove timers from auth hooks test, fix password test * remove timer from create account test * add timeout * cleanup --------- Co-authored-by: Brian Feister * Remove the PersistentCache functionality (#949) * Initial pass at PersistentCache * Drop test coverage * Change test * Update customer baskets cache when there is a basket mutation (#945) * update customer baskets cache on basket mutations * Fix layout shift for mega menu (#952) * remove custom styling * remove theme in theme file * spinners in drawer menu should be visible * Serialize category data once only (#953) * serialize data only once * remove console log --------- Co-authored-by: Brian Feister <47546998+bfeister@users.noreply.github.com> * chore: update pwa-kit-dev eslint config (#950) + Bump `eslint-plugin-react` to latest version + Auto-detect React version Co-authored-by: Adam Raya * resolving testing/merge errors * Add Shopper Experience hooks (#958) * Initial commit * Update license * Update test project to use bjnl_dev * Fix usePage hook and Refactor test page * Add usePages hook * Add usePages pdp and plp test cases * Clean up * Update Changelog & Restore `zzrf-001` config --------- Co-authored-by: Ben Chypak * add changelog * Apply changes from commerce react sdk in feature branch (#964) * apply change from commerce react sdk in feature branch * Remove wrong changlog * keep the same as develop * linting * fix formatMessage * bump sizes temporarily * fix product list tests * fix header tests * fix add to cart modal * skip tests and low test coverate temporarily * linting --------- Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> Co-authored-by: Alex Vuong <52219283+alexvuong@users.noreply.github.com> Co-authored-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: Vincent Marta Co-authored-by: Pavel <65617653+GoodNightBuddy@users.noreply.github.com> Co-authored-by: Snyk bot Co-authored-by: Alex Vuong Co-authored-by: yunakim714 <84923642+yunakim714@users.noreply.github.com> Co-authored-by: Brian Feister Co-authored-by: Adam Raya Co-authored-by: yunakim714 Co-authored-by: Will Harney Co-authored-by: Brian Feister <47546998+bfeister@users.noreply.github.com> Co-authored-by: echessman <37908171+echessman@users.noreply.github.com> Co-authored-by: CC ProdSec <65211003+cc-prodsec@users.noreply.github.com> Co-authored-by: Ben Chypak Co-authored-by: Oliver Brook Co-authored-by: Brad Adams Co-authored-by: Kieran Haberstock <80915722+kieran-sf@users.noreply.github.com> Co-authored-by: Ben Chypak --- .circleci/config.yml | 473 ----- .gitattributes | 2 +- .github/actions/check_clean/action.yml | 12 + .github/actions/count_deps/action.yml | 18 + .github/actions/create_mrt/action.yml | 14 + .github/actions/datadog/action.yml | 16 + .github/actions/lighthouse_ci/action.yml | 7 + .github/actions/publish_to_npm/action.yml | 21 + .github/actions/push_to_mrt/action.yml | 18 + .github/actions/setup_ubuntu/action.yml | 35 + .github/actions/setup_windows/action.yml | 14 + .github/actions/smoke_tests/action.yml | 14 + .github/actions/snyk/action.yml | 15 + .github/actions/unit_tests/action.yml | 27 + .github/workflows/test.yml | 279 +++ .gitignore | 3 +- lerna.json | 2 +- package-lock.json | 2 +- package.json | 2 +- packages/commerce-sdk-react/.prettierrc.yaml | 1 + packages/commerce-sdk-react/CHANGELOG.md | 7 +- packages/commerce-sdk-react/LICENSE | 2 +- packages/commerce-sdk-react/package-lock.json | 235 +-- packages/commerce-sdk-react/package.json | 7 +- .../commerce-sdk-react/scripts/version.js | 5 +- .../commerce-sdk-react/src/auth/index.test.ts | 87 +- packages/commerce-sdk-react/src/auth/index.ts | 99 +- .../src/auth/storage.test.ts | 61 + .../commerce-sdk-react/src/auth/storage.ts | 91 +- .../src/hooks/ShopperBaskets/mutation.test.ts | 45 +- .../src/hooks/ShopperBaskets/mutation.ts | 98 +- .../hooks/ShopperCustomers/mutation.test.tsx | 2 +- .../src/hooks/ShopperCustomers/mutation.ts | 47 +- .../src/hooks/ShopperExperience/index.ts | 7 + .../src/hooks/ShopperExperience/query.ts | 82 + .../src/hooks/ShopperLogin/helper.ts | 5 +- .../src/hooks/ShopperOrders/mutation.ts | 23 +- .../commerce-sdk-react/src/hooks/index.ts | 1 + .../commerce-sdk-react/src/hooks/types.ts | 2 + .../src/hooks/useCustomerType.ts | 4 +- .../commerce-sdk-react/src/hooks/utils.ts | 17 +- packages/commerce-sdk-react/src/provider.tsx | 2 + .../commerce-sdk-react/src/test-utils.tsx | 17 +- packages/commerce-sdk-react/src/utils.ts | 11 + packages/internal-lib-build/.prettierrc.yaml | 1 + packages/internal-lib-build/package-lock.json | 364 ++-- packages/internal-lib-build/package.json | 6 +- packages/pwa-kit-create-app/.prettierrc.yaml | 1 + packages/pwa-kit-create-app/package-lock.json | 1574 ++++++----------- packages/pwa-kit-create-app/package.json | 4 +- packages/pwa-kit-dev/.prettierrc.yaml | 1 + packages/pwa-kit-dev/CHANGELOG.md | 6 +- packages/pwa-kit-dev/bin/pwa-kit-dev.js | 231 ++- packages/pwa-kit-dev/package-lock.json | 322 ++-- packages/pwa-kit-dev/package.json | 10 +- packages/pwa-kit-dev/scripts/build-request.js | 64 - .../pwa-kit-dev/scripts/build-request.test.js | 110 -- packages/pwa-kit-dev/scripts/file-utils.js | 79 - .../pwa-kit-dev/scripts/file-utils.test.js | 184 -- packages/pwa-kit-dev/scripts/upload.js | 77 - packages/pwa-kit-dev/scripts/upload.test.js | 85 - packages/pwa-kit-dev/scripts/utils.js | 323 ---- packages/pwa-kit-dev/scripts/utils.test.js | 335 ---- packages/pwa-kit-dev/scripts/version.js | 5 +- .../src/configs/eslint/eslint-config.js | 2 +- .../src/configs/jest/jest.config.js | 7 +- .../src/ssr/server/build-dev-server.test.js | 49 +- packages/pwa-kit-dev/src/utils/glob.js | 75 - packages/pwa-kit-dev/src/utils/glob.test.js | 57 - .../src/utils/script-utils.test.js | 297 ++++ .../pwa-kit-dev/src/utils/script-utils.ts | 342 ++++ .../test-fixtures/minimal-built-app/ssr.js | 8 + .../minimal-built-app/static/favicon.ico | Bin 0 -> 5430 bytes packages/pwa-kit-react-sdk/.prettierrc.yaml | 1 + packages/pwa-kit-react-sdk/CHANGELOG.md | 4 +- packages/pwa-kit-react-sdk/README.md | 4 +- packages/pwa-kit-react-sdk/package-lock.json | 52 +- packages/pwa-kit-react-sdk/package.json | 6 +- .../scripts/file-utils.test.js | 18 +- .../pwa-kit-react-sdk/scripts/setup-jsdom.js | 2 +- packages/pwa-kit-react-sdk/scripts/version.js | 5 +- .../src/ssr/browser/main.test.js | 2 +- .../src/ssr/server/react-rendering.js | 5 +- .../src/ssr/server/react-rendering.test.js | 2 +- .../components/route-component/index.js | 9 +- .../components/route-component/index.test.js | 14 +- .../with-legacy-get-props/index.test.js | 2 +- .../components/with-react-query/index.test.js | 2 +- .../src/ssr/universal/contexts/index.test.js | 2 +- packages/pwa-kit-runtime/.prettierrc.yaml | 1 + packages/pwa-kit-runtime/CHANGELOG.md | 5 +- packages/pwa-kit-runtime/jest.config.js | 2 +- packages/pwa-kit-runtime/package-lock.json | 514 +++--- packages/pwa-kit-runtime/package.json | 10 +- packages/pwa-kit-runtime/scripts/version.js | 5 +- .../src/ssr/server/build-remote-server.js | 24 +- .../src/ssr/server/express.lambda.test.js | 23 +- .../src/ssr/server/express.test.js | 77 +- .../server/test_fixtures/server-renderer.js | 5 +- .../pwa-kit-runtime/src/utils/ssr-cache.js | 695 +------- .../src/utils/ssr-cache.test.js | 513 +----- .../pwa-kit-runtime/src/utils/ssr-proxying.js | 5 +- .../src/utils/ssr-proxying.test.js | 6 +- .../src/utils/ssr-server.test.js | 21 +- .../utils/ssr-server/outgoing-request-hook.js | 2 +- .../update-global-agent-options.test.js | 20 + .../src/utils/ssr-server/utils.test.js | 8 + .../template-express-minimal/.prettierrc.yaml | 1 + .../template-express-minimal/app/ssr.test.js | 5 +- .../package-lock.json | 64 +- .../template-express-minimal/package.json | 6 +- .../.prettierrc.yaml | 1 + .../template-retail-react-app/CHANGELOG.md | 8 + .../app/commerce-api/auth.js | 10 +- .../app/commerce-api/einstein.test.js | 33 +- .../app/commerce-api/hooks/useBasket.js | 5 + .../app/commerce-api/hooks/useCustomer.js | 1 - .../app/commerce-api/index.js | 2 +- .../app/commerce-api/index.test.js | 12 + .../app/commerce-api/mock-data.js | 278 +-- .../commerce-api/mocks/basket-with-suit.js | 6 +- .../commerce-api/mocks/einstein-mock-data.js | 111 +- .../mocks/variant-750518699578M.js | 21 +- .../app/commerce-api/pkce.js | 10 +- .../app/commerce-api/utils.js | 76 +- .../app/commerce-api/utils.test.js | 7 + .../app/components/_app/index.jsx | 28 +- .../app/components/drawer-menu/index.jsx | 121 +- .../app/components/drawer-menu/index.test.js | 56 +- .../app/components/header/index.test.js | 237 ++- .../components/image-gallery/index.test.js | 90 +- .../app/components/list-menu/index.jsx | 83 +- .../app/components/list-menu/index.test.js | 67 +- .../app/components/nested-accordion/index.jsx | 2 +- .../app/components/product-item/index.jsx | 9 +- .../components/recommended-products/index.jsx | 4 +- .../app/components/search/index.test.js | 32 +- .../app/components/swatch-group/index.test.js | 9 +- .../app/constants.js | 15 +- .../app/contexts/index.js | 77 +- .../app/hooks/use-add-to-cart-modal.js | 9 +- .../app/hooks/use-add-to-cart-modal.test.js | 89 +- .../app/hooks/use-auth-modal.js | 45 +- .../app/hooks/use-auth-modal.test.js | 266 ++- .../app/hooks/use-current-basket.js | 6 +- .../app/hooks/use-product-view-modal.test.js | 48 +- .../hooks/use-variation-attributes.test.js | 6 +- .../app/hooks/use-wishlist.test.js | 18 +- .../app/pages/account/addresses.jsx | 15 +- .../app/pages/account/addresses.test.js | 23 +- .../app/pages/account/index.jsx | 11 +- .../app/pages/account/index.test.js | 23 +- .../app/pages/account/order-detail.jsx | 3 +- .../app/pages/account/order-history.jsx | 10 +- .../app/pages/account/orders.test.js | 8 + .../app/pages/account/payments.test.js | 108 +- .../app/pages/account/wishlist/index.test.js | 18 +- .../wishlist-secondary-button-group.test.js | 18 +- .../app/pages/cart/index.jsx | 2 +- .../app/pages/cart/index.test.js | 95 +- .../app/pages/checkout/confirmation.test.js | 182 +- .../pages/checkout/partials/contact-info.jsx | 4 +- .../checkout/partials/payment-selection.jsx | 3 +- .../partials/shipping-address-selection.jsx | 3 +- .../app/pages/home/data.js | 3 +- .../app/pages/login/index.jsx | 15 +- .../app/pages/login/index.test.js | 83 +- .../app/pages/product-detail/index.jsx | 4 +- .../app/pages/product-detail/index.test.js | 16 +- .../app/pages/product-list/index.jsx | 15 +- .../app/pages/product-list/index.test.js | 58 +- .../app/pages/registration/index.test.jsx | 93 +- .../app/pages/reset-password/index.jsx | 9 +- .../app/pages/reset-password/index.test.jsx | 29 +- .../app/partials/product-view/index.jsx | 12 +- .../app/theme/components/project/list-menu.js | 1 - .../app/utils/responsive-image.test.js | 3 +- .../app/utils/routes-utils.test.js | 2 +- .../app/utils/site-utils.test.js | 6 +- .../app/utils/test-utils.js | 2 +- .../app/utils/url.js | 10 +- .../app/utils/url.test.js | 2 +- .../app/utils/utils.js | 9 +- .../app/utils/utils.test.js | 18 +- .../template-retail-react-app/jest-setup.js | 41 +- .../template-retail-react-app/jest.config.js | 4 +- .../package-lock.json | 720 ++++---- .../template-retail-react-app/package.json | 14 +- .../.prettierrc.yaml | 1 + .../package-lock.json | 118 +- .../template-typescript-minimal/package.json | 8 +- .../test-commerce-sdk-react/.prettierrc.yaml | 1 + .../app/pages/home.tsx | 3 + .../app/pages/use-product-search.tsx | 6 +- .../app/pages/use-promotions-for-campaign.tsx | 6 +- .../app/pages/use-shopper-categories.tsx | 6 +- .../app/pages/use-shopper-customer.tsx | 4 +- .../app/pages/use-shopper-experience.tsx | 95 + .../app/pages/use-shopper-products.tsx | 6 +- .../test-commerce-sdk-react/app/routes.tsx | 5 + .../test-commerce-sdk-react/package-lock.json | 2 +- packages/test-commerce-sdk-react/package.json | 10 +- scripts/assets/.prettierrc.yaml | 1 + 203 files changed, 5377 insertions(+), 7181 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/actions/check_clean/action.yml create mode 100644 .github/actions/count_deps/action.yml create mode 100644 .github/actions/create_mrt/action.yml create mode 100644 .github/actions/datadog/action.yml create mode 100644 .github/actions/lighthouse_ci/action.yml create mode 100644 .github/actions/publish_to_npm/action.yml create mode 100644 .github/actions/push_to_mrt/action.yml create mode 100644 .github/actions/setup_ubuntu/action.yml create mode 100644 .github/actions/setup_windows/action.yml create mode 100644 .github/actions/smoke_tests/action.yml create mode 100644 .github/actions/snyk/action.yml create mode 100644 .github/actions/unit_tests/action.yml create mode 100644 .github/workflows/test.yml create mode 100644 packages/commerce-sdk-react/src/auth/storage.test.ts create mode 100644 packages/commerce-sdk-react/src/hooks/ShopperExperience/index.ts create mode 100644 packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts create mode 100644 packages/commerce-sdk-react/src/utils.ts delete mode 100644 packages/pwa-kit-dev/scripts/build-request.js delete mode 100644 packages/pwa-kit-dev/scripts/build-request.test.js delete mode 100644 packages/pwa-kit-dev/scripts/file-utils.js delete mode 100644 packages/pwa-kit-dev/scripts/file-utils.test.js delete mode 100644 packages/pwa-kit-dev/scripts/upload.js delete mode 100644 packages/pwa-kit-dev/scripts/upload.test.js delete mode 100644 packages/pwa-kit-dev/scripts/utils.js delete mode 100644 packages/pwa-kit-dev/scripts/utils.test.js delete mode 100644 packages/pwa-kit-dev/src/utils/glob.js delete mode 100644 packages/pwa-kit-dev/src/utils/glob.test.js create mode 100644 packages/pwa-kit-dev/src/utils/script-utils.test.js create mode 100644 packages/pwa-kit-dev/src/utils/script-utils.ts create mode 100644 packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/ssr.js create mode 100644 packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/static/favicon.ico create mode 100644 packages/pwa-kit-runtime/src/utils/ssr-server/update-global-agent-options.test.js create mode 100644 packages/test-commerce-sdk-react/app/pages/use-shopper-experience.tsx diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 5ae7bc7125..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,473 +0,0 @@ -version: 2.1 -orbs: - slack: circleci/slack@3.2.0 - win: circleci/windows@2.4.1 - -commands: - setup: - description: 'Setup Machine' - steps: - - restore_cache: - keys: - - npm-cache - - run: - name: 'Set environment variables' - command: | - touch $BASH_ENV - if [[ "${CIRCLE_BRANCH}" == "develop" ]]; then echo 'export DEVELOP=true' >> $BASH_ENV; fi - if [[ "${CIRCLE_BRANCH}" =~ ^release- ]]; then echo 'export RELEASE=true' >> $BASH_ENV; fi - source $BASH_ENV - - run: - name: 'Install Dependencies' - command: | - - # Install system dependencies - sudo apt-get update - sudo apt-get install python-dev python-pip time - sudo pip install -U pip setuptools - sudo pip install awscli==1.18.85 datadog==0.40.1 - sudo npm install -g npm@6.14.4 - - # Install node dependencies - node ./scripts/gtime.js monorepo_install npm ci - - # Build the PWA - npm run lerna -- run analyze-build --scope "retail-react-app" - - # Report bundle sizes - node ./scripts/report-bundle-size.js - - # Check that packages are all using the same versions of compilers, etc. - node ./scripts/check-dependencies.js - - # Install Snyk CLI - sudo npm install -g snyk - - # Install Lighthouse CI CLI - sudo npm install -g @lhci/cli - - save_cache: - key: npm-cache - paths: - - /root/.npm - setup_windows: - description: 'Setup Machine' - steps: - - restore_cache: - keys: - - npm-cache - - run: - name: 'Install Dependencies' - command: | - - # Install node dependencies - npm ci - - save_cache: - key: npm-cache - paths: - - /root/.npm - setup_npm_7: - description: 'Setup Machine with npm 7' - steps: - - restore_cache: - keys: - - npm-cache - - run: - name: 'Install Dependencies' - command: | - - sudo npm install -g npm@7 - npm ci - - save_cache: - key: npm-cache - paths: - - /root/.npm - setup_npm_8: - description: 'Setup Machine with npm 8' - steps: - - restore_cache: - keys: - - npm-cache - - run: - name: 'Install Dependencies' - command: | - - sudo npm install -g npm@8 - npm ci - - save_cache: - key: npm-cache - paths: - - /root/.npm - runtests: - description: 'Run tests' - parameters: - cwd: - description: 'The directory to execute the tests from' - default: ${PWD} - type: string - steps: - - run: - name: 'Run tests' - command: | - # Explicitly set pipefile policy. This is the default for non-windows, but seems - # that is needs to be set on windows to fail immediately. - set -eo pipefail - - cd << parameters.cwd >> - - # Note: Each of these test commands need to be exposed on the monorepo - # root and *also* on the PWA package. This section is run on both. - - # Ensure bundlesize is in check - npm run test:max-file-size - - # Always run fast unit tests - npm run test - smoketestscripts: - description: 'Smoke test scripts' - parameters: - dir: - description: 'The path to a project to test' - default: './packages/template-retail-react-app' - type: string - steps: - - run: - name: 'Smoke test scripts' - command: | - # Basic smoke-tests for uncommonly run scripts in a project - node ./scripts/smoke-test-npm-scripts.js --dir << parameters.dir >> - lighthouse-ci: - description: 'Run Lighthouse CI on the PWA' - steps: - - run: - name: Run Lighthouse CI on the PWA - command: | - npm run test:lighthouse --prefix packages/template-retail-react-app - checkclean: - description: 'Check Repository Clean' - steps: - - run: - name: Check Repository Clean - command: | - # Print status for debugging on CircleCI. - git status - # Fail the build if any step leaves uncommitted changes in the repo - # that would prevent Lerna from publishing (Lerna gets this right). - git diff --exit-code - - generate_and_test_project: - description: 'Generate project' - steps: - - run: - name: Generate project - environment: - GENERATOR_PRESET: 'test-project' - command: | - set GENERATOR_PRESET=test-project - node packages/pwa-kit-create-app/scripts/create-mobify-app-dev.js --outputDir generated-project - cd generated-project - no_output_timeout: 5m - - runtests: - cwd: generated-project - - smoketestscripts: - dir: generated-project - - run: - name: Count Generated Project Dependencies - environment: - command: | - MAX_PACKAGES="2260" - total=$(./scripts/count-dependencies.js generated-project) - echo "export TOTAL_PACKAGES=${total}" >> $BASH_ENV; - source $BASH_ENV - - if [ "$total" -gt "$MAX_PACKAGES" ]; then - echo "Error: Found $TOTAL_PACKAGES installed packages (max $MAX_PACKAGES)."; - exit 1; - else - echo "Found $TOTAL_PACKAGES installed packages (max $MAX_PACKAGES)."; - fi - - create_mrt_credentials_file: - steps: - - run: - name: Create MRT credentials file - command: | - # Add credentials file at ~/.mobify so we can upload to Mobify Cloud - npm run save-credentials --prefix packages/template-retail-react-app -- --user "${MOBIFY_CLIENT_USER}" --key "${MOBIFY_CLIENT_API_KEY}" - - early_return_for_forked_pull_requests: - description: >- - If this build is from a fork, stop executing the current job and return success. - This is useful to avoid steps that will fail due to missing credentials. - steps: - - run: - name: Early return if this build is from a forked PR - command: | - if [ -n "$CIRCLE_PR_NUMBER" ]; then - echo "Nothing else to do for forked PRs, so marking this step successful" - circleci step halt - fi - - generator_store_verdaccio_log_file_artifact: - steps: - - store_artifacts: - path: packages/pwa-kit-create-app/local-npm-repo/verdaccio.log - destination: local-npm-repo/verdaccio.log - -jobs: - docs_build_and_deploy: - docker: - - image: circleci/node:14-stretch-browsers - steps: - - add_ssh_keys: - fingerprints: - - "1d:96:b9:15:51:84:09:7b:77:98:f8:ec:51:5b:43:1b" - - checkout - - setup - - run: - name: "Build and publish docs" - command: | - if [[ $RELEASE ]]; then - git config user.email "bot-pwa-kit@salesforce.com" - git config user.name "bot-pwa-kit" - cd packages/commerce-sdk-react - node ./scripts/build-and-release-docs.js - fi - testNode14Windows: - executor: - name: win/default - size: "large" - shell: bash.exe - steps: - - checkout - - setup_windows - - runtests - testNode14npm7: - docker: - - image: circleci/node:14-stretch-browsers - steps: - - checkout - - setup_npm_7 - - runtests - testNode14npm8: - docker: - - image: circleci/node:14-stretch-browsers - steps: - - checkout - - setup_npm_8 - - runtests - testNode14: - docker: - - image: circleci/node:14-stretch-browsers - steps: - - checkout - - setup - - runtests - - lighthouse-ci - - smoketestscripts - - early_return_for_forked_pull_requests - - create_mrt_credentials_file - - run: - name: Push Bundle - command: | - if [[ $DEVELOP || $RELEASE ]]; then - target=staging - target_commerce_sdk_react="commerce-sdk-react" - elif [[ $RELEASE ]]; then - target=production - else - target="" - fi - - project="scaffold-pwa" - - if [[ $target ]]; then - npm run push --prefix packages/template-retail-react-app -- -s $project --message "build ${CIRCLE_BUILD_NUM} on ${CIRCLE_BRANCH} (${CIRCLE_SHA1})" --target $target - fi - if [[ $target_commerce_sdk_react ]]; then - npm run push --prefix packages/test-commerce-sdk-react -- -s $project --message "test-commerce-sdk-react build ${CIRCLE_BUILD_NUM} on ${CIRCLE_BRANCH} (${CIRCLE_SHA1})" --target $target_commerce_sdk_react - fi - - checkclean - - run: - name: Publish to NPM - command: | - # Add NPM token to allow publishing from Circle - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc - - if [[ $RELEASE ]]; then - # Publish all changed packages. The "from-package" arg means "look - # at the version numbers in each package.json file and if that doesn't - # exist on NPM, publish" - npm run lerna -- publish from-package --yes --no-verify-access - fi - - # Cleanup - rm ~/.npmrc - - store_test_results: - path: packages/template-retail-react-app/tests/reports - when: always - - store_artifacts: - path: packages/template-retail-react-app/tests/screenshots - when: always - - slack/status: - fail_only: true - only_for_branches: develop - generatedTemplate: - docker: - - image: circleci/node:14-stretch-browsers - parameters: - template: - type: string - steps: - - checkout - - setup - - run: - name: Generate << parameters.template >> project - environment: - GENERATOR_PRESET: << parameters.template >> - command: | - set GENERATOR_PRESET=<< parameters.template >> - node packages/pwa-kit-create-app/scripts/create-mobify-app-dev.js --outputDir generated-<< parameters.template >> - no_output_timeout: 5m - - generator_store_verdaccio_log_file_artifact - generatedTemplateWindows: - parameters: - template: - type: string - executor: - name: win/default - size: "medium" - shell: bash.exe - steps: - - checkout - - setup_windows - - run: - name: Generate << parameters.template >> project - environment: - GENERATOR_PRESET: << parameters.template >> - command: | - set GENERATOR_PRESET=<< parameters.template >> - node packages/pwa-kit-create-app/scripts/create-mobify-app-dev.js --outputDir generated-<< parameters.template >> - no_output_timeout: 5m - - generator_store_verdaccio_log_file_artifact - generatedSFCCProjectTest: - docker: - - image: circleci/node:14-stretch-browsers - steps: - - checkout - - setup - # TODO: un-comment this - # - generate_and_test_project - - - early_return_for_forked_pull_requests - - create_mrt_credentials_file - - slack/status: - fail_only: true - only_for_branches: develop - - run: - name: Audit Generated Project - environment: - command: | - # This is the *most* important audit step, much more than the - # "whole monorepo" one above - bear in mind that SDK users probably - # don't care if eg. our docs site uses a vulnerable dependency. - # - # Treat failures here seriously and try to fix them before just bumping - # thresholds. These vulnerabilities create a really bad impression with our - # users during sales or in the early stages of a project. - # - # REMINDER: Have you bumped up our alpha versions? If you recently fixed some - # vulnerabilities, you will need to increment the alpha versions (so that - # publishing them to the local npm repo would be successful). - - if [[ "${CIRCLE_BRANCH}" == "develop" ]]; then - # Run snyk auth - authenticate snyk using environment variables to add the token - snyk auth $SNYK_TOKEN - - snyk monitor --ignore-policy --remote-repo-url='https://github.com/SalesforceCommerceCloud/pwa-kit.git' --project-name='generated-scaffold-pwa' - fi - # TODO: un-comment this block - # - run: - # name: Send metrics to Datadog - # environment: - # command: | - # # Add a dogrc so we can submit metrics to datadog - # printf "[Connection]\napikey = $DATADOG_API_KEY\nappkey =\n" > ~/.dogrc - - # dog metric post mobify_platform_sdks.generated_project_total_packages $TOTAL_PACKAGES - - run: - name: Push Bundle - command: | - if [[ $DEVELOP ]]; then - target='generated-pwa' - else - target="" - fi - - project="scaffold-pwa" - - if [[ $target ]]; then - cd generated-project - npm run push -- -s $project --message "Generated PWA build ${CIRCLE_BUILD_NUM} on ${CIRCLE_BRANCH} (${CIRCLE_SHA1})" --target $target - fi - - generator_store_verdaccio_log_file_artifact - generatedSFCCProjectTestWindows: - executor: - name: win/default - size: "large" - shell: bash.exe - steps: - - checkout - - setup_windows - # TODO: un-comment this - # - generate_and_test_project - - generator_store_verdaccio_log_file_artifact -workflows: - version: 2 - test: - jobs: - - testNode14 - - testNode14Windows - - testNode14npm7 - - testNode14npm8 - - generatedSFCCProjectTest - - generatedSFCCProjectTestWindows - - generatedTemplate: - matrix: - parameters: - template: [express-minimal-test-project, typescript-minimal-test-project] - - generatedTemplateWindows: - matrix: - parameters: - template: [express-minimal-test-project, typescript-minimal-test-project] - - docs_build_and_deploy - nightly-build: - triggers: - - schedule: - cron: "0 8 * * *" - filters: - branches: - only: - - develop - jobs: - - testNode14: - context: nightly-build - - testNode14Windows: - context: nightly-build - - testNode14npm7: - context: nightly-build - - testNode14npm8: - context: nightly-build - - generatedSFCCProjectTest: - context: nightly-build - - generatedSFCCProjectTestWindows: - context: nightly-build - - generatedTemplate: - context: nightly-build - matrix: - parameters: - template: [ express-minimal-test-project, typescript-minimal-test-project ] - - generatedTemplateWindows: - context: nightly-build - matrix: - parameters: - template: [ express-minimal-test-project, typescript-minimal-test-project ] diff --git a/.gitattributes b/.gitattributes index e4e38f98a7..4eb50d10fe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ **/package-lock.json binary linguist-generated=true -* text=auto +* text=auto eol=lf diff --git a/.github/actions/check_clean/action.yml b/.github/actions/check_clean/action.yml new file mode 100644 index 0000000000..484958480f --- /dev/null +++ b/.github/actions/check_clean/action.yml @@ -0,0 +1,12 @@ +name: check_clean +runs: + using: composite + steps: + - name: Check Repository Clean + run: |- + # Print status for debugging on CircleCI. + git status + # Fail the build if any step leaves uncommitted changes in the repo + # that would prevent Lerna from publishing (Lerna gets this right). + git diff --exit-code + shell: bash diff --git a/.github/actions/count_deps/action.yml b/.github/actions/count_deps/action.yml new file mode 100644 index 0000000000..f5c84584c2 --- /dev/null +++ b/.github/actions/count_deps/action.yml @@ -0,0 +1,18 @@ +name: count_deps +runs: + using: composite + steps: + - name: Count Generated Project Dependencies + # TODO: Can TOTAL_PACKAGES be exported in a cleaner way? + run: |- + MAX_PACKAGES="2260" + total=$(./scripts/count-dependencies.js generated-${{ matrix.template }}) + echo "TOTAL_PACKAGES=${total}" >> $GITHUB_ENV + + if [ "$total" -gt "$MAX_PACKAGES" ]; then + echo "Error: Found $TOTAL_PACKAGES installed packages (max $MAX_PACKAGES)."; + exit 1; + else + echo "Found $TOTAL_PACKAGES installed packages (max $MAX_PACKAGES)."; + fi + shell: bash diff --git a/.github/actions/create_mrt/action.yml b/.github/actions/create_mrt/action.yml new file mode 100644 index 0000000000..9c9f153884 --- /dev/null +++ b/.github/actions/create_mrt/action.yml @@ -0,0 +1,14 @@ +name: create_mrt +inputs: + mobify_user: + description: "Mobify user email" + mobify_api_key: + description: "Mobify user API key" +runs: + using: composite + steps: + - name: Create MRT credentials file + run: |- + # Add credentials file at ~/.mobify so we can upload to Mobify Cloud + npm run save-credentials --prefix packages/template-retail-react-app -- --user "${{inputs.mobify_user}}" --key "${{inputs.mobify_api_key}}" + shell: bash diff --git a/.github/actions/datadog/action.yml b/.github/actions/datadog/action.yml new file mode 100644 index 0000000000..a35bcc51fc --- /dev/null +++ b/.github/actions/datadog/action.yml @@ -0,0 +1,16 @@ +name: datadog +inputs: + datadog_api_key: + description: "Datadog API key" + TOTAL_PACKAGES: + description: "Total # of packages" +runs: + using: composite + steps: + - name: Send metrics to Datadog + run : | + # Add a dogrc so we can submit metrics to datadog + printf "[Connection]\napikey = ${{inputs.datadog_api_key}}\nappkey =\n" > ~/.dogrc + + dog metric post mobify_platform_sdks.generated_project_total_packages ${{ inputs.TOTAL_PACKAGES }} + shell: bash diff --git a/.github/actions/lighthouse_ci/action.yml b/.github/actions/lighthouse_ci/action.yml new file mode 100644 index 0000000000..3b6474959a --- /dev/null +++ b/.github/actions/lighthouse_ci/action.yml @@ -0,0 +1,7 @@ +name: lighthouse_ci +runs: + using: composite + steps: + - name: Run Lighthouse CI on the PWA + run: npm run test:lighthouse --prefix packages/template-retail-react-app + shell: bash diff --git a/.github/actions/publish_to_npm/action.yml b/.github/actions/publish_to_npm/action.yml new file mode 100644 index 0000000000..0f9267ea5c --- /dev/null +++ b/.github/actions/publish_to_npm/action.yml @@ -0,0 +1,21 @@ +name: publish_to_npm +inputs: + NODE_AUTH_TOKEN: + description: "Node auth token" +runs: + using: composite + steps: + # TODO: Figure out a way to specify whether to publish to "latest" or "next" tag + - name: Publish to NPM + run: |- + # Add NPM token to allow publishing + echo "//registry.npmjs.org/:_authToken=${{ inputs.NODE_AUTH_TOKEN }}" > ~/.npmrc + + # Publish all changed packages. The "from-package" arg means "look + # at the version numbers in each package.json file and if that doesn't + # exist on NPM, publish" + npm run lerna -- publish from-package --yes --no-verify-access + + # Cleanup + rm ~/.npmrc + shell: bash diff --git a/.github/actions/push_to_mrt/action.yml b/.github/actions/push_to_mrt/action.yml new file mode 100644 index 0000000000..c7affda607 --- /dev/null +++ b/.github/actions/push_to_mrt/action.yml @@ -0,0 +1,18 @@ +name: push_to_mrt +inputs: + CWD: + description: Project directory + TARGET: + description: MRT target +runs: + using: composite + steps: + - name: Push Bundle to MRT + run: |- + cd ${{ inputs.CWD }} + project="scaffold-pwa" + build="build ${{ github.run_id }} on ${{ github.ref }} (${{ github.sha }})" + if [[ ${{ inputs.TARGET }} ]]; then + npm run push -- -s $project --message "$build" --target ${{ inputs.TARGET }} + fi + shell: bash diff --git a/.github/actions/setup_ubuntu/action.yml b/.github/actions/setup_ubuntu/action.yml new file mode 100644 index 0000000000..87b24e699e --- /dev/null +++ b/.github/actions/setup_ubuntu/action.yml @@ -0,0 +1,35 @@ +name: setup_ubuntu +inputs: + cwd: + required: false + default: "${PWD}" +description: "Setup Ubuntu Machine" +runs: + using: composite + steps: + - name: Install Dependencies + run: |- + # Install system dependencies + sudo apt-get update -yq + sudo apt-get install python2 python3-pip time -yq + sudo pip install -U pip setuptools + sudo pip install awscli==1.18.85 datadog==0.40.1 + + # Install node dependencies + node ./scripts/gtime.js monorepo_install npm ci + + # Build the PWA + npm run lerna -- run analyze-build --scope "retail-react-app" + + # Report bundle sizes + node ./scripts/report-bundle-size.js + + # Check that packages are all using the same versions of compilers, etc. + node ./scripts/check-dependencies.js + + # Install Snyk CLI + sudo npm install -g snyk + + # Install Lighthouse CI CLI + sudo npm install -g @lhci/cli + shell: bash diff --git a/.github/actions/setup_windows/action.yml b/.github/actions/setup_windows/action.yml new file mode 100644 index 0000000000..bea04deb09 --- /dev/null +++ b/.github/actions/setup_windows/action.yml @@ -0,0 +1,14 @@ +name: setup_windows +inputs: + cwd: + required: false + default: "${PWD}" +description: "Setup Windows Machine" +runs: + using: composite + steps: + - name: Install Dependencies + run: |- + # Install node dependencies + npm ci + shell: bash diff --git a/.github/actions/smoke_tests/action.yml b/.github/actions/smoke_tests/action.yml new file mode 100644 index 0000000000..135e6aca25 --- /dev/null +++ b/.github/actions/smoke_tests/action.yml @@ -0,0 +1,14 @@ +name: smoke_tests +inputs: + dir: + required: false + # The path to a project to test + default: "./packages/template-retail-react-app" +runs: + using: composite + steps: + - name: Smoke test scripts + run: |- + # Basic smoke-tests for uncommonly run scripts in a project + node ./scripts/smoke-test-npm-scripts.js --dir ${{ inputs.dir }} + shell: bash diff --git a/.github/actions/snyk/action.yml b/.github/actions/snyk/action.yml new file mode 100644 index 0000000000..fb3cbe0541 --- /dev/null +++ b/.github/actions/snyk/action.yml @@ -0,0 +1,15 @@ +name: snyk +inputs: + snyk_token: + description: "Snyk token" + DEVELOP: + description: "Is this the 'develop' branch?" +runs: + using: composite + steps: + - name: Audit Generated Project + run: |- + # Run snyk auth - authenticate snyk using environment variables to add the token + snyk auth ${{ inputs.snyk_token }} + snyk monitor --ignore-policy --remote-repo-url='https://github.com/SalesforceCommerceCloud/pwa-kit.git' --project-name='generated-scaffold-pwa' + shell: bash diff --git a/.github/actions/unit_tests/action.yml b/.github/actions/unit_tests/action.yml new file mode 100644 index 0000000000..2b7bbe010c --- /dev/null +++ b/.github/actions/unit_tests/action.yml @@ -0,0 +1,27 @@ +name: unit_tests +inputs: + cwd: + required: false + default: "${PWD}" +description: "Run tests action description" +runs: + using: composite + steps: + - name: Run tests step + # TODO: The pilefile policy is a legacy of CircleCI. Is it still needed? + run: |- + # Explicitly set pipefile policy. This is the default for non-windows, but seems + # that is needs to be set on windows to fail immediately. + set -eo pipefail + + cd ${{ inputs.cwd }} + + # Note: Each of these test commands need to be exposed on the monorepo + # root and *also* on the PWA package. This section is run on both. + + # Ensure bundlesize is in check + npm run test:max-file-size + + # Always run fast unit tests + npm run test + shell: bash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..d5f5bc68ce --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,279 @@ +# WARNING! Conditionals are set as variables to minimize repetitive checks. +# However, this results in the variables being the *string* values "true" or "false". +# As a result, you must always explicitly check for those strings. For example, +# ${{ env.DEVELOP }} will ALWAYS evaluate as true; to achieve the expected result +# you must check ${{ env.DEVELOP == 'true' }}. There's probably a better way to DRY, +# but this is what we have for now. + +name: SalesforceCommerceCloud/pwa-kit/test +on: + # PRs from forks trigger `pull_request`, but do NOT have access to secrets. + # More info: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflows-in-forked-repositories-1 + # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + # https://docs.github.com/en/actions/managing-workflow-runs/approving-workflow-runs-from-public-forks + pull_request: # Default: opened, reopened, synchronize (head branch updated) + push: + branches: + - develop + # TODO: Should we run on all pushes to release branches, or should we run on GitHub releases? + - 'release-*' + schedule: + # Run every day at 12pm (PST) - cron uses UTC times + - cron: '0 8 * * *' +env: + IS_NOT_FORK: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }} + DEVELOP: ${{ (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && (github.head_ref || github.ref_name) == 'develop' }} + RELEASE: ${{ (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && startsWith(github.head_ref || github.ref_name, 'release-') }} + +jobs: + pwa-kit: + strategy: + matrix: + node: [14] + npm: [6, 7, 8] + runs-on: ubuntu-latest + env: + # The "default" npm is the one that ships with a given version of node + # node v14 uses npm@6, latest node v16 uses npm@8 + # For more: https://nodejs.org/en/download/releases/ + IS_DEFAULT_NPM: ${{ matrix.npm == 6 }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + cache: npm + + - name: Update NPM version + if: env.IS_DEFAULT_NPM == 'false' + run: |- + npm install -g npm@${{ matrix.npm }} + + - name: Setup Ubuntu Machine + uses: "./.github/actions/setup_ubuntu" + + - name: Run unit tests + uses: "./.github/actions/unit_tests" + + - name: Run Lighthouse CI on the PWA + if: env.IS_DEFAULT_NPM == 'true' + uses: "./.github/actions/lighthouse_ci" + + - name: Smoke test scripts + if: env.IS_DEFAULT_NPM == 'true' + uses: "./.github/actions/smoke_tests" + + - name: Create MRT credentials file + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.DEVELOP == 'true' + uses: "./.github/actions/create_mrt" + with: + mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} + mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} + + - name: Push Bundle to MRT (Development) + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.DEVELOP == 'true' + uses: "./.github/actions/push_to_mrt" + with: + CWD: "./packages/template-retail-react-app" + TARGET: staging + + - name: Push Bundle to MRT (Production) + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.RELEASE == 'true' + uses: "./.github/actions/push_to_mrt" + with: + CWD: "./packages/template-retail-react-app" + TARGET: production + + - name: Push Bundle to MRT (Commerce SDK React) + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.DEVELOPMENT == 'true' + uses: "./.github/actions/push_to_mrt" + with: + CWD: "./packages/test-commerce-sdk-react" + TARGET: commerce-sdk-react + + - name: Check Repository Clean + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' + uses: "./.github/actions/check_clean" + + - name: Publish to NPM + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.RELEASE == 'true' + uses: "./.github/actions/publish_to_npm" + with: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} + + - name: Send GitHub Action data to Slack workflow (PWA Kit) + id: slack + if: env.IS_NOT_FORK == 'true' && env.IS_DEFAULT_NPM == 'true' && env.DEVELOP == 'true' && failure() + uses: slackapi/slack-github-action@v1.23.0 + with: + payload: | + { + "test": "testNode${{ matrix.node }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + pwa-kit-windows: + strategy: + # TODO: We don't *need* a matrix with single values, + # but is it worth keeping for supporting multiple versions in the future? + matrix: + node: [14] + npm: [6] + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + cache: npm + + - name: Setup Windows Machine + uses: "./.github/actions/setup_windows" + + - name: Run tests + uses: "./.github/actions/unit_tests" + + # TODO: The generated workflow is identical to the generated-windows workflow, + # with a few extra steps. Can the workflows be merged? (Add `os` to the matrix?) + generated: + strategy: + matrix: + template: [test-project, retail-react-app-demo, express-minimal-test-project, typescript-minimal-test-project] + runs-on: ubuntu-latest + env: + IS_TEMPLATE_FROM_RETAIL_REACT_APP: ${{ matrix.template == 'test-project' || matrix.template == 'retail-react-app-demo' }} + PROJECT_DIR: generated-${{ matrix.template }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 14 + + - name: Setup Ubuntu Machine + uses: "./.github/actions/setup_ubuntu" + + - name: Generate ${{ matrix.template }} project + run: |- + node packages/pwa-kit-create-app/scripts/create-mobify-app-dev.js --outputDir ${{ env.PROJECT_DIR }} + env: + GENERATOR_PRESET: ${{ matrix.template }} + timeout-minutes: 5 + + - name: Run unit tests + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/unit_tests" + with: + cwd: ${{ env.PROJECT_DIR }} + + - name: Run smoke tests + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/smoke_tests" + with: + dir: ${{ env.PROJECT_DIR }} + + - name: Count Generated Project Dependencies + id: count_deps + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/count_deps" + + - name: Store Verdaccio logfile artifact + uses: actions/upload-artifact@v3 + with: + path: packages/pwa-kit-create-app/local-npm-repo/verdaccio.log + + - name: Audit Generated Project + if: env.IS_NOT_FORK == 'true' && env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' && env.DEVELOP == 'true' + uses: "./.github/actions/snyk" + with: + snyk_token: ${{ secrets.SNYK_TOKEN }} + + - name: Send metrics to Datadog + if: env.IS_NOT_FORK == 'true' && env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/datadog" + with: + datadog_api_key: ${{ secrets.DATADOG_API_KEY }} + # TODO: The way this is set is a little bit magic - can it be cleaned up? + TOTAL_PACKAGES: $TOTAL_PACKAGES + + - name: Create MRT credentials file + if: env.IS_NOT_FORK == 'true' && env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' && env.DEVELOP == 'true' + uses: "./.github/actions/create_mrt" + with: + mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} + mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} + + - name: Push Bundle to MRT + if: env.IS_NOT_FORK == 'true' && env.DEVELOP == 'true' && matrix.template == 'test-project' + uses: "./.github/actions/push_to_mrt" + with: + CWD: ${{ env.PROJECT_DIR }} + TARGET: generated-pwa + + - name: Send GitHub Action data to Slack workflow (Generated) + id: slack + if: env.IS_NOT_FORK == 'true' && env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' && env.DEVELOP == 'true' && failure() + uses: slackapi/slack-github-action@v1.23.0 + with: + payload: | + { + "test": "generated ${{ matrix.template }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + generated-windows: + strategy: + matrix: + template: [test-project, retail-react-app-demo, express-minimal-test-project, typescript-minimal-test-project] + runs-on: windows-latest + env: + IS_TEMPLATE_FROM_RETAIL_REACT_APP: ${{ matrix.template == 'test-project' || matrix.template == 'retail-react-app-demo' }} + PROJECT_DIR: generated-${{ matrix.template }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 14 + + - name: Setup Windows Machine + uses: "./.github/actions/setup_windows" + + - name: Generate ${{ matrix.template }} project + run: |- + node packages/pwa-kit-create-app/scripts/create-mobify-app-dev.js --outputDir generated-${{ matrix.template }} + env: + GENERATOR_PRESET: ${{ matrix.template }} + timeout-minutes: 7 + + - name: Run unit tests + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/unit_tests" + with: + cwd: ${{ env.PROJECT_DIR }} + + - name: Run smoke tests + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/smoke_tests" + with: + dir: ${{ env.PROJECT_DIR }} + + - name: Count Generated Project Dependencies + if: env.IS_TEMPLATE_FROM_RETAIL_REACT_APP == 'true' + uses: "./.github/actions/count_deps" + + - name: Store Verdaccio logfile artifact + uses: actions/upload-artifact@v3 + with: + path: packages/pwa-kit-create-app/local-npm-repo/verdaccio.log diff --git a/.gitignore b/.gitignore index 884fb55bb8..86d285f055 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ coverage/ packages/*/out packages/*/docs/www lerna-debug.log -.idea/ \ No newline at end of file +.idea/ +.vscode/ \ No newline at end of file diff --git a/lerna.json b/lerna.json index 5e353feb6a..c36be6d5d1 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages": [ "packages/*" ], - "version": "2.6.0-dev", + "version": "2.7.0-dev", "publish": { "allowBranch": [ "master" diff --git a/package-lock.json b/package-lock.json index d7f10ae51c..11254ef02b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pwa-kit", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6590f85215..31bbbb1791 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pwa-kit", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "engines": { "node": "^14.0.0", "npm": "^6.14.4 || ^7.0.0 || ^8.0.0" diff --git a/packages/commerce-sdk-react/.prettierrc.yaml b/packages/commerce-sdk-react/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/commerce-sdk-react/.prettierrc.yaml +++ b/packages/commerce-sdk-react/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/commerce-sdk-react/CHANGELOG.md b/packages/commerce-sdk-react/CHANGELOG.md index 2542903a55..4d340b7345 100644 --- a/packages/commerce-sdk-react/CHANGELOG.md +++ b/packages/commerce-sdk-react/CHANGELOG.md @@ -1,4 +1,9 @@ -## v2.6.0-dev (Jan 05, 2023) +## v2.7.0-dev (Jan 25, 2023) +- Namespace `Auth` storage keys with site identifier to allow multi-site support [#911](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/911) +- Add Shopper Experience `usePage` and `usePages` hooks[#958](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/958) + +## v2.6.0 (Jan 25, 2023) + ## v2.5.0 (Jan 05, 2023) - Exclude test files in package file to avoid publishing them [#856](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/856) - Pass in 'headers' and 'rawResponse' options to mutation hooks [#845](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/845) diff --git a/packages/commerce-sdk-react/LICENSE b/packages/commerce-sdk-react/LICENSE index d8f701f7ed..d7d70cb7ea 100644 --- a/packages/commerce-sdk-react/LICENSE +++ b/packages/commerce-sdk-react/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2021, Salesforce.com, Inc. +Copyright (c) 2023, Salesforce.com, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/packages/commerce-sdk-react/package-lock.json b/packages/commerce-sdk-react/package-lock.json index 1909fabe46..c64ba9ea74 100644 --- a/packages/commerce-sdk-react/package-lock.json +++ b/packages/commerce-sdk-react/package-lock.json @@ -1,13 +1,13 @@ { "name": "commerce-sdk-react-preview", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { "@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.1.0.tgz", + "integrity": "sha512-mMVJ/j/GbZ/De4ZHWbQAQO1J6iVnjtZLc9WEdkUQb8S/Bu2cAF2bETXUgMAdvMG3/ngtKmcNBe+Zms9bg6jnQQ==", "dev": true }, "@babel/code-frame": { @@ -89,39 +89,39 @@ } }, "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "dev": true, "requires": { "regenerator-runtime": "^0.13.11" } }, "@jest/expect-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", - "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.1.tgz", + "integrity": "sha512-w6YJMn5DlzmxjO00i9wu2YSozUYRBhIoJ6nQwpMYcBMtiqMGJm1QBzOf6DDgRao8dbtpDoaqLg6iiQTvv0UHhQ==", "dev": true, "requires": { "jest-get-type": "^29.2.0" } }, "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz", + "integrity": "sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ==", "dev": true, "requires": { - "@sinclair/typebox": "^0.24.1" + "@sinclair/typebox": "^0.25.16" } }, "@jest/types": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", - "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.1.tgz", + "integrity": "sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA==", "dev": true, "requires": { - "@jest/schemas": "^29.0.0", + "@jest/schemas": "^29.4.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -142,9 +142,9 @@ } }, "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "version": "0.25.21", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", + "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==", "dev": true }, "@tanstack/match-sorter-utils": { @@ -157,25 +157,25 @@ } }, "@tanstack/query-core": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.0.tgz", - "integrity": "sha512-OeLyBKBQoT265f5G9biReijeP8mBxNFwY7ZUu1dKL+YzqpG5q5z7J/N1eT8aWyKuhyDTiUHuKm5l+oIVzbtrjw==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.24.4.tgz", + "integrity": "sha512-9dqjv9eeB6VHN7lD3cLo16ZAjfjCsdXetSAD5+VyKqLUvcKTL0CklGQRJu+bWzdrS69R6Ea4UZo8obHYZnG6aA==", "dev": true }, "@tanstack/react-query": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.22.0.tgz", - "integrity": "sha512-P9o+HjG42uB/xHR6dMsJaPhtZydSe4v0xdG5G/cEj1oHZAXelMlm67/rYJNQGKgBamKElKogj+HYGF+NY2yHYg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.24.4.tgz", + "integrity": "sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==", "dev": true, "requires": { - "@tanstack/query-core": "4.22.0", + "@tanstack/query-core": "4.24.4", "use-sync-external-store": "^1.2.0" } }, "@tanstack/react-query-devtools": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.22.0.tgz", - "integrity": "sha512-YeYFBnfqvb+ZlA0IiJqiHNNSzepNhI1p2o9i8NlhQli9+Zrn230M47OBaBUs8qr3DD1dC2zGB1Dis50Ktz8gAA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-4.24.4.tgz", + "integrity": "sha512-4mldcR99QDX8k94I+STM9gPsYF+FDAD2EQJvHtxR2HrDNegbfmY474xuW0QUZaNW/vJi09Gak6b6Vy2INWhL6w==", "dev": true, "requires": { "@tanstack/match-sorter-utils": "^8.7.0", @@ -184,9 +184,9 @@ } }, "@testing-library/dom": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.1.tgz", - "integrity": "sha512-P6iIPyYQ+qH8CvGauAqanhVnjrnRe0IZFSYCeGkSRW9q3u8bdVn2NPI+lasFyVsEQn1J/IFmp5Aax41+dAP9wg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", + "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", @@ -305,9 +305,9 @@ } }, "@types/jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", + "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", "dev": true, "requires": { "expect": "^29.0.0", @@ -351,9 +351,9 @@ "dev": true }, "@types/react": { - "version": "17.0.52", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.52.tgz", - "integrity": "sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==", + "version": "17.0.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.53.tgz", + "integrity": "sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==", "dev": true, "requires": { "@types/prop-types": "*", @@ -392,9 +392,9 @@ } }, "@types/yargs": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", - "integrity": "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==", + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -508,9 +508,9 @@ "dev": true }, "commerce-sdk-isomorphic": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/commerce-sdk-isomorphic/-/commerce-sdk-isomorphic-1.8.0.tgz", - "integrity": "sha512-vjWJ4KPNXq3ckZkc9fkYDkrT6LZcGIrQZ9nmjQXbg5V5B9K72lmBV+uZQQVdYVX+qI4PfXY4JEO6mfKMiTgkCA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/commerce-sdk-isomorphic/-/commerce-sdk-isomorphic-1.9.0.tgz", + "integrity": "sha512-fT0s/u4EEnE7R7JXGOTFc0NACpxb9CEJokZF7UI2RNlpZMb3hmvdUoBdXDuxv1Pfg6zG813Gk98J7nH5cwLOnw==", "requires": { "cross-fetch": "^3.1.5", "nanoid": "^3.3.4" @@ -626,9 +626,9 @@ "dev": true }, "dom-accessibility-api": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.15.tgz", - "integrity": "sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true }, "ecdsa-sig-formatter": { @@ -641,19 +641,20 @@ } }, "es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" } }, "escape-string-regexp": { @@ -672,16 +673,16 @@ } }, "expect": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", - "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz", + "integrity": "sha512-OKrGESHOaMxK3b6zxIq9SOW8kEXztKff/Dvg88j4xIJxur1hspEbedVkR3GpHe5LO+WB2Qw7OWN0RMTdp6as5A==", "dev": true, "requires": { - "@jest/expect-utils": "^29.3.1", + "@jest/expect-utils": "^29.4.1", "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.3.1", - "jest-message-util": "^29.3.1", - "jest-util": "^29.3.1" + "jest-matcher-utils": "^29.4.1", + "jest-message-util": "^29.4.1", + "jest-util": "^29.4.1" } }, "fill-range": { @@ -715,9 +716,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -791,6 +792,17 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "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", @@ -975,15 +987,15 @@ "dev": true }, "jest-diff": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", - "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.1.tgz", + "integrity": "sha512-uazdl2g331iY56CEyfbNA0Ut7Mn2ulAG5vUaEHXycf1L6IPyuImIxSz4F0VYBKi7LYIuxOwTZzK3wh5jHzASMw==", "dev": true, "requires": { "chalk": "^4.0.0", "diff-sequences": "^29.3.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" + "pretty-format": "^29.4.1" }, "dependencies": { "chalk": { @@ -1005,15 +1017,15 @@ "dev": true }, "jest-matcher-utils": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", - "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.1.tgz", + "integrity": "sha512-k5h0u8V4nAEy6lSACepxL/rw78FLDkBnXhZVgFneVpnJONhb2DhZj/Gv4eNe+1XqQ5IhgUcqj745UwH0HJmMnA==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.3.1", + "jest-diff": "^29.4.1", "jest-get-type": "^29.2.0", - "pretty-format": "^29.3.1" + "pretty-format": "^29.4.1" }, "dependencies": { "chalk": { @@ -1029,18 +1041,18 @@ } }, "jest-message-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", - "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.1.tgz", + "integrity": "sha512-H4/I0cXUaLeCw6FM+i4AwCnOwHRgitdaUFOdm49022YD5nfyr8C/DrbXOBEyJaj+w/y0gGJ57klssOaUiLLQGQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.3.1", + "@jest/types": "^29.4.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.3.1", + "pretty-format": "^29.4.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -1116,12 +1128,12 @@ } }, "jest-util": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", - "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.1.tgz", + "integrity": "sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ==", "dev": true, "requires": { - "@jest/types": "^29.3.1", + "@jest/types": "^29.4.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -1278,9 +1290,9 @@ "dev": true }, "marked": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz", - "integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true }, "merge": { @@ -1306,9 +1318,9 @@ "dev": true }, "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -1338,9 +1350,9 @@ "dev": true }, "nock": { - "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.0.tgz", + "integrity": "sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg==", "dev": true, "requires": { "debug": "^4.1.0", @@ -1364,9 +1376,9 @@ "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==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "object-is": { @@ -1410,12 +1422,12 @@ "dev": true }, "pretty-format": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", - "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz", + "integrity": "sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg==", "dev": true, "requires": { - "@jest/schemas": "^29.0.0", + "@jest/schemas": "^29.4.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -1585,6 +1597,15 @@ } } }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "requires": { + "internal-slot": "^1.0.4" + } + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -1595,9 +1616,9 @@ } }, "superjson": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.1.tgz", - "integrity": "sha512-HMTj43zvwW5bD+JCZCvFf4DkZQCmiLTen4C+W1Xogj0SPOpnhxsriogM04QmBVGH5b3kcIIOr6FqQ/aoIDx7TQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.2.tgz", + "integrity": "sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==", "dev": true, "requires": { "copy-anything": "^3.0.2" @@ -1639,9 +1660,9 @@ } }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, "use-sync-external-store": { diff --git a/packages/commerce-sdk-react/package.json b/packages/commerce-sdk-react/package.json index d23263ba65..b3a7ed3c58 100644 --- a/packages/commerce-sdk-react/package.json +++ b/packages/commerce-sdk-react/package.json @@ -1,6 +1,6 @@ { "name": "commerce-sdk-react-preview", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "description": "A library that provides react hooks for fetching data from Commerce Cloud", "author": "cc-pwa-kit@salesforce.com", "license": "See license in LICENSE", @@ -8,7 +8,6 @@ "node": "^14.0.0", "npm": "^6.14.4 || ^7.0.0 || ^8.0.0" }, - "private": true, "files": [ "CHANGELOG.md", "LICENSE", @@ -60,7 +59,7 @@ "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "cross-env": "^5.2.0", - "internal-lib-build": "^2.6.0-dev", + "internal-lib-build": "^2.7.0-dev", "jest-silent-reporter": "^0.5.0", "jsonwebtoken": "^8.5.1", "nock": "^13.2.9", @@ -75,7 +74,7 @@ "react": "^17" }, "dependencies": { - "commerce-sdk-isomorphic": "^1.8.0", + "commerce-sdk-isomorphic": "^1.9.0", "js-cookie": "^3.0.1", "jwt-decode": "^3.1.2" } diff --git a/packages/commerce-sdk-react/scripts/version.js b/packages/commerce-sdk-react/scripts/version.js index 6a9c724dd3..792011c28e 100644 --- a/packages/commerce-sdk-react/scripts/version.js +++ b/packages/commerce-sdk-react/scripts/version.js @@ -11,10 +11,7 @@ const path = require('path') const os = require('os') const fs = require('fs') -const date = new Date() - .toString() - .split(' ') - .slice(1, 4) +const date = new Date().toString().split(' ').slice(1, 4) const heading = `## v${pkg.version} (${date[0]} ${date[1]}, ${date[2]})\n` const changelog = path.resolve(os.tmpdir(), 'CHANGELOG.md') diff --git a/packages/commerce-sdk-react/src/auth/index.test.ts b/packages/commerce-sdk-react/src/auth/index.test.ts index 884ff4b2ac..c5920db3b0 100644 --- a/packages/commerce-sdk-react/src/auth/index.test.ts +++ b/packages/commerce-sdk-react/src/auth/index.test.ts @@ -7,37 +7,16 @@ import Auth from './' import jwt from 'jsonwebtoken' import {helpers} from 'commerce-sdk-isomorphic' +import * as utils from '../utils' +// Use memory storage for all our storage types. jest.mock('./storage', () => { + const originalModule = jest.requireActual('./storage') + return { - CookieStorage: jest.fn(function() { - const map = new Map() - return { - set(key: string, value: string) { - map.set(key, value) - }, - get(key: string) { - return map.get(key) - }, - delete(key: string) { - map.delete(key) - } - } - }), - LocalStorage: jest.fn(function() { - const map = new Map() - return { - set(key: string, value: string) { - map.set(key, value) - }, - get(key: string) { - return map.get(key) - }, - delete(key: string) { - map.delete(key) - } - } - }) + ...originalModule, + CookieStorage: originalModule.MemoryStorage, + LocalStorage: originalModule.MemoryStorage } }) @@ -55,6 +34,11 @@ jest.mock('commerce-sdk-isomorphic', () => { } }) +jest.mock('../utils', () => ({ + __esModule: true, + onClient: () => true +})) + const config = { clientId: 'clientId', organizationId: 'organizationId', @@ -79,6 +63,10 @@ describe('Auth', () => { auth.set('access_token', accessToken) expect(auth.get('refresh_token_guest')).toBe(refreshToken) expect(auth.get('access_token')).toBe(accessToken) + // @ts-expect-error private property + expect([...auth.stores['cookie'].map.keys()]).toEqual([`siteId_cc-nx-g`]) + // @ts-expect-error private property + expect([...auth.stores['local'].map.keys()]).toEqual([`siteId_access_token`]) }) test('set registered refresh token will clear guest refresh token, vise versa', () => { const auth = new Auth(config) @@ -89,10 +77,10 @@ describe('Auth', () => { auth.set('refresh_token_guest', refreshTokenGuest) // @ts-expect-error private method auth.set('refresh_token_registered', refreshTokenRegistered) - expect(auth.get('refresh_token_guest')).toBe(undefined) + expect(auth.get('refresh_token_guest')).toBe('') // @ts-expect-error private method auth.set('refresh_token_guest', refreshTokenGuest) - expect(auth.get('refresh_token_registered')).toBe(undefined) + expect(auth.get('refresh_token_registered')).toBe('') }) test('this.data returns the storage value', () => { const auth = new Auth(config) @@ -106,7 +94,8 @@ describe('Auth', () => { id_token: 'id_token', idp_access_token: 'idp_access_token', token_type: 'token_type', - usid: 'usid' + usid: 'usid', + customer_type: 'guest' } const {refresh_token_guest, ...result} = {...sample, refresh_token: 'refresh_token_guest'} @@ -161,7 +150,8 @@ describe('Auth', () => { id_token: 'id_token', idp_access_token: 'idp_access_token', token_type: 'token_type', - usid: 'usid' + usid: 'usid', + customer_type: 'guest' } // @ts-expect-error private method auth.pendingToken = Promise.resolve(data) @@ -180,7 +170,8 @@ describe('Auth', () => { id_token: 'id_token', idp_access_token: 'idp_access_token', token_type: 'token_type', - usid: 'usid' + usid: 'usid', + customer_type: 'guest' } const {refresh_token_guest, ...result} = {...data, refresh_token: 'refresh_token_guest'} @@ -203,7 +194,8 @@ describe('Auth', () => { id_token: 'id_token', idp_access_token: 'idp_access_token', token_type: 'token_type', - usid: 'usid' + usid: 'usid', + customer_type: 'guest' } const {refresh_token_guest, ...result} = {...data, refresh_token: 'refresh_token_guest'} @@ -241,4 +233,31 @@ describe('Auth', () => { expect(helpers.loginGuestUser).toBeCalled() }) }) + test('running on the server uses a shared context memory store', async () => { + const refreshTokenGuest = 'guest' + + // Mock running on the server so shared context storage is used. + // @ts-expect-error read-only property + utils.onClient = () => false + + // Create a new auth instance and set its guest token. + const authA = new Auth({...config, siteId: 'siteA'}) + // @ts-expect-error private method + authA.set('refresh_token_guest', refreshTokenGuest) + // @ts-expect-error private property + expect([...authA.stores['memory'].map.keys()]).toEqual([`siteA_cc-nx-g`]) + + // Create a second auth instance and ensure that its memory store has previous + // guest tokens set from the first store (this emulates a second lambda request.) + const authB = new Auth({...config, siteId: 'siteB'}) + // @ts-expect-error private method + authB.set('refresh_token_guest', refreshTokenGuest) + + // @ts-expect-error private property + expect([...authB.stores['memory'].map.keys()]).toEqual([`siteA_cc-nx-g`, `siteB_cc-nx-g`]) + + // Set mock value back to expected. + // @ts-expect-error read-only property + utils.onClient = () => true + }) }) diff --git a/packages/commerce-sdk-react/src/auth/index.ts b/packages/commerce-sdk-react/src/auth/index.ts index fa752ee0c8..42e0413010 100644 --- a/packages/commerce-sdk-react/src/auth/index.ts +++ b/packages/commerce-sdk-react/src/auth/index.ts @@ -4,11 +4,18 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import {helpers, ShopperLogin, ShopperCustomers, ShopperLoginTypes, ShopperCustomersTypes} from 'commerce-sdk-isomorphic' +import { + helpers, + ShopperLogin, + ShopperCustomers, + ShopperLoginTypes, + ShopperCustomersTypes +} from 'commerce-sdk-isomorphic' import jwtDecode from 'jwt-decode' import {ApiClientConfigParams, Argument} from '../hooks/types' -import {BaseStorage, LocalStorage, CookieStorage} from './storage' +import {BaseStorage, LocalStorage, CookieStorage, MemoryStorage, StorageType} from './storage' import {CustomerType} from '../hooks/useCustomerType' +import {onClient} from '../utils' type Helpers = typeof helpers interface AuthConfig extends ApiClientConfigParams { @@ -43,9 +50,9 @@ type AuthDataKeys = type AuthDataMap = Record< AuthDataKeys, { - storage: BaseStorage + storageType: StorageType key: string - callback?: () => void + callback?: (storage: BaseStorage) => void } > @@ -59,68 +66,64 @@ type AuthData = ShopperLoginTypes.TokenResponse & { customer_type: CustomerType } -const onClient = typeof window !== 'undefined' -const localStorage = onClient ? new LocalStorage() : new Map() -const cookieStorage = onClient ? new CookieStorage() : new Map() - /** * A map of the data that this auth module stores. This maps the name of the property to - * the storage and the key when stored in that storage. You can also pass in a "callback" + * the storage type and the key when stored in that storage. You can also pass in a "callback" * function to do extra operation after a property is set. */ const DATA_MAP: AuthDataMap = { access_token: { - storage: localStorage, + storageType: 'local', key: 'access_token' }, customer_id: { - storage: localStorage, + storageType: 'local', key: 'customer_id' }, usid: { - storage: localStorage, + storageType: 'local', key: 'usid' }, enc_user_id: { - storage: localStorage, + storageType: 'local', key: 'enc_user_id' }, expires_in: { - storage: localStorage, + storageType: 'local', key: 'expires_in' }, id_token: { - storage: localStorage, + storageType: 'local', key: 'id_token' }, idp_access_token: { - storage: localStorage, + storageType: 'local', key: 'idp_access_token' }, token_type: { - storage: localStorage, + storageType: 'local', key: 'token_type' }, refresh_token_guest: { - storage: cookieStorage, + storageType: 'cookie', key: 'cc-nx-g', - callback: () => { - cookieStorage.delete('cc-nx') + callback: (store) => { + store.delete('cc-nx') } }, refresh_token_registered: { - storage: cookieStorage, + storageType: 'cookie', key: 'cc-nx', - callback: () => { - cookieStorage.delete('cc-nx-g') + callback: (store) => { + store.delete('cc-nx-g') } }, site_id: { - storage: cookieStorage, + storageType: 'cookie', key: 'cc-site-id' }, customer_type: { - storage: localStorage, + storageType: 'local', key: 'customer_type' } } @@ -139,6 +142,7 @@ class Auth { private redirectURI: string private pendingToken: Promise | undefined private REFRESH_TOKEN_EXPIRATION_DAYS = 90 + private stores: Record constructor(config: AuthConfig) { this.client = new ShopperLogin({ @@ -164,38 +168,47 @@ class Auth { fetchOptions: config.fetchOptions }) - if (this.get('site_id') && this.get('site_id') !== config.siteId) { - // if site is switched, remove all existing auth data in storage - // and the next auth.ready() call with restart the auth flow - this.clearStorage() - this.pendingToken = undefined + const storageOptions = {keyPrefix: config.siteId} + const serverStorageOptions = { + keyPrefix: config.siteId, + sharedContext: true // This allows use to reused guest authentication tokens accross lambda runs. } - if (!this.get('site_id')) { - this.set('site_id', config.siteId, { - expires: this.REFRESH_TOKEN_EXPIRATION_DAYS - }) - } + this.stores = onClient() + ? { + cookie: new CookieStorage(storageOptions), + local: new LocalStorage(storageOptions), + memory: new MemoryStorage(storageOptions) + } + : { + // Always use MemoryStorage on the server. + cookie: new MemoryStorage(serverStorageOptions), + local: new MemoryStorage(serverStorageOptions), + memory: new MemoryStorage(serverStorageOptions) + } this.redirectURI = config.redirectURI } get(name: AuthDataKeys) { - const storage = DATA_MAP[name].storage - const key = DATA_MAP[name].key + const {key, storageType} = DATA_MAP[name] + const storage = this.stores[storageType] return storage.get(key) } private set(name: AuthDataKeys, value: string, options?: unknown) { - const {key, storage} = DATA_MAP[name] + const {key, storageType} = DATA_MAP[name] + const storage = this.stores[storageType] storage.set(key, value, options) - DATA_MAP[name].callback?.() + DATA_MAP[name].callback?.(storage) } private clearStorage() { - Object.keys(DATA_MAP).forEach((key) => { + Object.keys(DATA_MAP).forEach((keyName) => { type Key = keyof AuthDataMap - DATA_MAP[key as Key].storage.delete(DATA_MAP[key as Key].key) + const {key, storageType} = DATA_MAP[keyName as Key] + const store = this.stores[storageType] + store.delete(key) }) } @@ -335,9 +348,7 @@ class Auth { * This is a wrapper method for ShopperCustomer API registerCustomer endpoint. * */ - async register( - body: ShopperCustomersTypes.CustomerRegistration - ) { + async register(body: ShopperCustomersTypes.CustomerRegistration) { const { customer: {email}, password diff --git a/packages/commerce-sdk-react/src/auth/storage.test.ts b/packages/commerce-sdk-react/src/auth/storage.test.ts new file mode 100644 index 0000000000..b813a3167f --- /dev/null +++ b/packages/commerce-sdk-react/src/auth/storage.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import {BaseStorage, MemoryStorage} from './storage' + +const key = 'key' +const value = 'value' + +const testCases = [ + { + description: 'MemoryStorage works without options', + storageOptions: undefined, + validate: (storage: BaseStorage) => { + storage.set(key, value) + expect(storage.get(key)).toBe(value) + storage.delete(key) + expect(storage.get(key)).toBe('') + } + }, + { + description: 'MemoryStorage works with options', + storageOptions: { + keyPrefix: 'prefix', + keyPrefixSeparator: '$' + }, + validate: (storage: BaseStorage) => { + storage.set(key, value) + expect(storage.get(key)).toBe(value) + // @ts-expect-error private property + expect([...storage.map.keys()]).toEqual([`prefix$${key}`]) + storage.delete(key) + expect(storage.get(key)).toBe('') + } + }, + { + description: 'MemoryStorage works with with shared context', + storageOptions: { + sharedContext: true + }, + validate: (storage: BaseStorage) => { + storage.set(key, value) + expect(storage.get(key)).toBe(value) + const secondStore = new MemoryStorage({ + sharedContext: true + }) + expect(secondStore.get(key)).toBe(value) + } + } +] + +describe('Storage Classes', () => { + testCases.forEach(({description, storageOptions, validate}) => { + test(description, () => { + const storage = new MemoryStorage(storageOptions) + validate(storage) + }) + }) +}) diff --git a/packages/commerce-sdk-react/src/auth/storage.ts b/packages/commerce-sdk-react/src/auth/storage.ts index ec228547cd..a6cab30a27 100644 --- a/packages/commerce-sdk-react/src/auth/storage.ts +++ b/packages/commerce-sdk-react/src/auth/storage.ts @@ -6,10 +6,32 @@ */ import Cookies from 'js-cookie' -export interface BaseStorage { - set(key: string, value: string, options?: unknown): void - get(key: string): string - delete(key: string): void +export type StorageType = 'cookie' | 'local' | 'memory' + +export interface BaseStorageOptions { + keyPrefix?: string + keyPrefixSeparator?: string +} + +export interface MemoryStorageOptions extends BaseStorageOptions { + sharedContext?: boolean +} +export abstract class BaseStorage { + protected options: Required + + constructor(options: BaseStorageOptions = {keyPrefixSeparator: '_'}) { + this.options = { + keyPrefixSeparator: options.keyPrefix ? options.keyPrefixSeparator ?? '_' : '', + keyPrefix: options.keyPrefix ?? '' + } + } + + protected getPrefixedKey(key: string): string { + return `${this.options.keyPrefix}${this.options.keyPrefixSeparator}${key}` + } + abstract set(key: string, value: string, options?: unknown): void + abstract get(key: string): string + abstract delete(key: string): void } /** @@ -18,20 +40,25 @@ export interface BaseStorage { * or a customized storage. This class is mainly used for commerce-sdk-react library * to store authentication tokens. */ -export class CookieStorage implements BaseStorage { - constructor() { +export class CookieStorage extends BaseStorage { + constructor(options?: BaseStorageOptions) { + super(options) + if (typeof document === 'undefined') { throw new Error('CookieStorage is not avaliable on the current environment.') } } set(key: string, value: string, options?: Cookies.CookieAttributes) { - Cookies.set(key, value, {...options, secure: true}) + const prefixedKey = this.getPrefixedKey(key) + Cookies.set(prefixedKey, value, {...options, secure: true}) } get(key: string) { - return Cookies.get(key) || '' + const prefixedKey = this.getPrefixedKey(key) + return Cookies.get(prefixedKey) || '' } delete(key: string) { - Cookies.remove(key) + const prefixedKey = this.getPrefixedKey(key) + Cookies.remove(prefixedKey) } } @@ -41,33 +68,61 @@ export class CookieStorage implements BaseStorage { * or a customized storage. This class is mainly used for commerce-sdk-react library * to store authentication tokens. */ -export class LocalStorage implements BaseStorage { - constructor() { +export class LocalStorage extends BaseStorage { + constructor(options?: BaseStorageOptions) { + super(options) + if (typeof window === 'undefined') { throw new Error('LocalStorage is not avaliable on the current environment.') } } set(key: string, value: string) { - const oldValue = this.get(key) - window.localStorage.setItem(key, value) + const prefixedKey = this.getPrefixedKey(key) + const oldValue = this.get(prefixedKey) + window.localStorage.setItem(prefixedKey, value) const event = new StorageEvent('storage', { - key: key, + key: prefixedKey, oldValue: oldValue, newValue: value }) window.dispatchEvent(event) } get(key: string) { - return window.localStorage.getItem(key) || '' + const prefixedKey = this.getPrefixedKey(key) + return window.localStorage.getItem(prefixedKey) || '' } delete(key: string) { - const oldValue = this.get(key) - window.localStorage.removeItem(key) + const prefixedKey = this.getPrefixedKey(key) + const oldValue = this.get(prefixedKey) + window.localStorage.removeItem(prefixedKey) const event = new StorageEvent('storage', { - key: key, + key: prefixedKey, oldValue: oldValue, newValue: null }) window.dispatchEvent(event) } } + +const globalMap = new Map() + +export class MemoryStorage extends BaseStorage { + private map: Map + constructor(options?: MemoryStorageOptions) { + super(options) + + this.map = options?.sharedContext ? globalMap : new Map() + } + set(key: string, value: string) { + const prefixedKey = this.getPrefixedKey(key) + this.map.set(prefixedKey, value) + } + get(key: string) { + const prefixedKey = this.getPrefixedKey(key) + return this.map.get(prefixedKey) || '' + } + delete(key: string) { + const prefixedKey = this.getPrefixedKey(key) + this.map.delete(prefixedKey) + } +} diff --git a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.test.ts b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.test.ts index fbe8103ee5..28788ff7b9 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.test.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.test.ts @@ -13,8 +13,6 @@ import { assertUpdateQuery, DEFAULT_TEST_HOST, mockMutationEndpoints, - NEW_DATA, - OLD_DATA, renderHookWithProviders } from '../../test-utils' import { @@ -23,7 +21,7 @@ import { useShopperBasketsMutation } from './mutation' import {useBasket} from './query' -import {useCustomerBaskets} from '../ShopperCustomers/query' +import {useCustomerBaskets} from '../ShopperCustomers' import {CacheUpdateMatrixElement} from '../utils' const CUSTOMER_ID = 'CUSTOMER_ID' @@ -113,7 +111,25 @@ const mutationPayloads: MutationPayloads = { body: {id: '001'} } } +const oldCustomerBaskets = { + total: 1, + baskets: [{basketId: BASKET_ID, hello: 'world'}] +} + +const newCustomerBaskets = { + total: 1, + baskets: [{basketId: BASKET_ID, hello: 'world_modified'}] +} +const oldBasket = { + basketId: BASKET_ID, + hello: 'world' +} + +const newBasket = { + basketId: BASKET_ID, + hello: 'world_modified' +} const tests = (Object.keys(mutationPayloads) as ShopperBasketsMutationType[]).map( (mutationName) => { const payload = mutationPayloads[mutationName] @@ -124,7 +140,11 @@ const tests = (Object.keys(mutationPayloads) as ShopperBasketsMutationType[]).ma { name: 'success', assertions: async () => { - mockMutationEndpoints('/checkout/shopper-baskets/') + mockMutationEndpoints( + '/checkout/shopper-baskets/', + {errorResponse: 200}, + newBasket + ) mockRelatedQueries() const {result, waitForValueToChange} = renderHookWithProviders(() => { @@ -151,21 +171,24 @@ const tests = (Object.keys(mutationPayloads) as ShopperBasketsMutationType[]).ma await waitForValueToChange(() => result.current.mutation.isSuccess) expect(result.current.mutation.isSuccess).toBe(true) - // On successful mutation, the query cache gets updated too. Let's assert it. const cacheUpdateMatrix = getCacheUpdateMatrix(CUSTOMER_ID) // @ts-ignore const matrixElement = cacheUpdateMatrix[mutationName](payload, {}) const {invalidate, update, remove}: CacheUpdateMatrixElement = matrixElement + const assertionData = { + basket: newBasket, + customerBaskets: newCustomerBaskets + } update?.forEach(({name}) => { // @ts-ignore - assertUpdateQuery(result.current.queries[name], NEW_DATA) + assertUpdateQuery(result.current.queries[name], assertionData[name]) }) invalidate?.forEach(({name}) => { // @ts-ignore - assertInvalidateQuery(result.current.queries[name], OLD_DATA) + assertInvalidateQuery(result.current.queries[name], oldCustomerBaskets) }) remove?.forEach(({name}) => { @@ -221,24 +244,24 @@ const mockRelatedQueries = () => { .get((uri) => { return uri.includes(basketEndpoint) }) - .reply(200, OLD_DATA) + .reply(200, oldBasket) nock(DEFAULT_TEST_HOST) .persist() .get((uri) => { return uri.includes(basketEndpoint) }) - .reply(200, NEW_DATA) + .reply(200, newBasket) // For get customer basket nock(DEFAULT_TEST_HOST) .get((uri) => { return uri.includes(customerEndpoint) }) - .reply(200, OLD_DATA) + .reply(200, oldCustomerBaskets) nock(DEFAULT_TEST_HOST) .persist() .get((uri) => { return uri.includes(customerEndpoint) }) - .reply(200, NEW_DATA) + .reply(200, newCustomerBaskets) } diff --git a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.ts b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.ts index 583030730a..0925c06668 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/mutation.ts @@ -11,6 +11,7 @@ import {CacheUpdateMatrixElement, NotImplementedError, updateCache} from '../uti import useCustomerId from '../useCustomerId' type Client = ApiClients['shopperBaskets'] +type CustomerClient = ApiClients['shopperCustomers'] export const ShopperBasketsMutations = { /** @@ -182,21 +183,53 @@ type UseShopperBasketsMutationArg = { } type ShopperBasketsClient = ApiClients['shopperBaskets'] -export type ShopperBasketsMutationType = typeof ShopperBasketsMutations[keyof typeof ShopperBasketsMutations] +export type ShopperBasketsMutationType = + (typeof ShopperBasketsMutations)[keyof typeof ShopperBasketsMutations] /** * @private */ export const getCacheUpdateMatrix = (customerId: string | null) => { - const updateBasketQuery = (basketId?: string) => { + const updateBasketQuery = ( + basketId?: string, + response?: DataType + ) => { // TODO: we're missing headers, rawResponse -> not only {basketId} const arg = {basketId} + return basketId ? { update: [ { name: 'basket', - key: ['/baskets', basketId, arg] + key: ['/baskets', basketId, arg], + updater: () => response + }, + { + // Since we use baskets from customer basket query, we need to update it for any basket mutation + name: 'customerBaskets', + key: ['/customers', customerId, '/baskets', {customerId}], + updater: ( + oldData: NonNullable> + ) => { + // do not update if responded basket does not exist inside existing customer baskets + if ( + !oldData?.baskets?.some( + (basket) => basket.basketId === response?.basketId + ) + ) { + return undefined + } + const updatedBaskets = oldData.baskets?.map( + (basket: DataType) => { + return basket?.basketId === basketId ? response : basket + } + ) + return { + ...oldData, + baskets: updatedBaskets + } + } } ] } @@ -240,8 +273,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, addItemToBasket: ( @@ -251,8 +283,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, removeItemFromBasket: ( @@ -262,8 +293,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params?.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, addPaymentInstrumentToBasket: ( @@ -273,18 +303,15 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, createBasket: ( params: Argument, response: DataType ): CacheUpdateMatrixElement => { - const basketId = response.basketId - return { - ...updateBasketQuery(basketId), + // we want to re-fetch basket data in this case to get the basket total and other baskets data ...invalidateCustomerBasketsQuery(customerId) } }, @@ -303,10 +330,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { params: Argument, response: DataType ): CacheUpdateMatrixElement => { - const basketId = response.basketId - return { - ...updateBasketQuery(basketId), ...invalidateCustomerBasketsQuery(customerId) } }, @@ -317,8 +341,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params?.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, removePaymentInstrumentFromBasket: ( @@ -328,8 +351,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params?.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateBasket: ( @@ -339,8 +361,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateBillingAddressForBasket: ( @@ -350,8 +371,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateCustomerForBasket: ( @@ -361,8 +381,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateItemInBasket: ( @@ -372,8 +391,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updatePaymentInstrumentInBasket: ( @@ -383,8 +401,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateShippingAddressForShipment: ( @@ -394,8 +411,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } }, updateShippingMethodForShipment: ( @@ -405,8 +421,7 @@ export const getCacheUpdateMatrix = (customerId: string | null) => { const basketId = params.parameters.basketId return { - ...updateBasketQuery(basketId), - ...invalidateCustomerBasketsQuery(customerId) + ...updateBasketQuery(basketId, response) } } } @@ -445,16 +460,19 @@ export function useShopperBasketsMutation( (params, apiClients) => { const method = apiClients['shopperBaskets'][action] as MutationFunction - return (method.call as ( - apiClient: ShopperBasketsClient, - params: Params, - rawResponse: boolean | undefined - ) => any)(apiClients['shopperBaskets'], {...params, headers}, rawResponse) + return ( + method.call as ( + apiClient: ShopperBasketsClient, + params: Params, + rawResponse: boolean | undefined + ) => any + )(apiClients['shopperBaskets'], {...params, headers}, rawResponse) }, { onSuccess: (data, params) => { diff --git a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.test.tsx b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.test.tsx index 7bc578359a..5deafda214 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.test.tsx +++ b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.test.tsx @@ -178,7 +178,7 @@ const tests = (Object.keys(mutationPayloads) as ShopperCustomersMutationType[]). ] queryKeys.forEach(({key: queryKey}: QueryKeyMap) => { - queryClient.setQueryData(queryKey, {test: true}) + queryClient.setQueryData(queryKey, () => ({test: true})) }) const button = screen.getByRole('button', { diff --git a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.ts b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.ts index af2075e196..49f161a5be 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/mutation.ts @@ -226,7 +226,13 @@ export const shopperCustomersCacheUpdateMatrix = { ): CacheUpdateMatrixElement => { const {customerId} = params.parameters return { - update: [{name: 'customer', key: ['/customers', customerId, {customerId}]}], + update: [ + { + name: 'customer', + key: ['/customers', customerId, {customerId}], + updater: () => response + } + ], invalidate: [ { name: 'customerPaymentInstrument', @@ -247,7 +253,8 @@ export const shopperCustomersCacheUpdateMatrix = { update: [ { name: 'customerAddress', - key: ['/customers', customerId, '/addresses', {addressName, customerId}] + key: ['/customers', customerId, '/addresses', {addressName, customerId}], + updater: () => response } ], invalidate: [{name: 'customer', key: ['/customers', customerId, {customerId}]}] @@ -269,7 +276,8 @@ export const shopperCustomersCacheUpdateMatrix = { customerId, '/addresses', {addressName: addressId, customerId} - ] + ], + updater: () => response } ], invalidate: [{name: 'customer', key: ['/customers', customerId, {customerId}]}] @@ -309,7 +317,8 @@ export const shopperCustomersCacheUpdateMatrix = { customerId, '/payment-instruments', {customerId, paymentInstrumentId: response?.paymentInstrumentId} - ] + ], + updater: () => response } ], invalidate: [{name: 'customer', key: ['/customers', customerId, {customerId}]}] @@ -354,7 +363,8 @@ export const shopperCustomersCacheUpdateMatrix = { customerId, '/product-list', {customerId, listId: response?.id} - ] + ], + updater: () => response } ] } @@ -369,7 +379,14 @@ export const shopperCustomersCacheUpdateMatrix = { update: [ { name: 'customerProductListItem', - key: ['/customers', customerId, '/product-list', listId, {itemId: response?.id}] + key: [ + '/customers', + customerId, + '/product-list', + listId, + {itemId: response?.id} + ], + updater: () => response } ], invalidate: [ @@ -390,7 +407,8 @@ export const shopperCustomersCacheUpdateMatrix = { update: [ { name: 'customerProductListItem', - key: ['/customers', customerId, '/product-list', listId, {itemId}] + key: ['/customers', customerId, '/product-list', listId, {itemId}], + updater: () => response } ], invalidate: [ @@ -438,7 +456,8 @@ export const SHOPPER_CUSTOMERS_NOT_IMPLEMENTED = [ 'updateCustomerProductList' ] -export type ShopperCustomersMutationType = typeof ShopperCustomersMutations[keyof typeof ShopperCustomersMutations] +export type ShopperCustomersMutationType = + (typeof ShopperCustomersMutations)[keyof typeof ShopperCustomersMutations] type UseShopperCustomersMutationHeaders = NonNullable< Argument @@ -472,11 +491,13 @@ function useShopperCustomersMutation( (params, apiClients) => { const method = apiClients['shopperCustomers'][action] as MutationFunction - return (method.call as ( - apiClient: ShopperCustomersClient, - params: Params, - rawResponse: boolean | undefined - ) => any)(apiClients['shopperCustomers'], {...params, headers}, rawResponse) + return ( + method.call as ( + apiClient: ShopperCustomersClient, + params: Params, + rawResponse: boolean | undefined + ) => any + )(apiClients['shopperCustomers'], {...params, headers}, rawResponse) }, { onSuccess: (data, params) => { diff --git a/packages/commerce-sdk-react/src/hooks/ShopperExperience/index.ts b/packages/commerce-sdk-react/src/hooks/ShopperExperience/index.ts new file mode 100644 index 0000000000..df1d7e713c --- /dev/null +++ b/packages/commerce-sdk-react/src/hooks/ShopperExperience/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2023, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +export * from './query' diff --git a/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts new file mode 100644 index 0000000000..5a09fe68af --- /dev/null +++ b/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import {ApiClients, Argument, DataType} from '../types' +import {useQuery} from '../useQuery' +import {UseQueryOptions, UseQueryResult} from '@tanstack/react-query' +import useConfig from '../useConfig' + +type Client = ApiClients['shopperExperience'] + +type UsePagesParameters = NonNullable>['parameters'] +type UsePagesHeaders = NonNullable>['headers'] +type UsePagesArg = {headers?: UsePagesHeaders; rawResponse?: boolean} & UsePagesParameters +/** + * A hook for `ShopperExperience#getPages`. + * Get Page Designer pages. The results will apply the visibility rules for each page's components, such as personalization or scheduled visibility. + * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-experience?meta=getPages} for more information about the API endpoint. + * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperexperience.shopperexperience-1.html#getpages} for more information on the parameters and returned data type. + * @returns An object describing the state of the request. + */ +function usePages( + arg: Omit & {rawResponse?: false}, + options?: UseQueryOptions | Response, Error> +): UseQueryResult, Error> +function usePages( + arg: Omit & {rawResponse: true}, + options?: UseQueryOptions | Response, Error> +): UseQueryResult +function usePages( + arg: UsePagesArg, + options?: UseQueryOptions | Response, Error> +): UseQueryResult | Response, Error> { + const {headers, rawResponse, ...parameters} = arg + const {locale} = useConfig() + parameters.locale = parameters.locale || locale + return useQuery( + ['/pages', arg], + (_, {shopperExperience}) => { + return shopperExperience.getPages({parameters, headers}, rawResponse) + }, + options + ) +} + +type UsePageParameters = NonNullable>['parameters'] +type UsePageHeaders = NonNullable>['headers'] +type UsePageArg = {headers?: UsePageHeaders; rawResponse?: boolean} & UsePageParameters +/** + * A hook for `ShopperExperience#getPage`. + * Get a Page Designer page based on a single page ID. The results will apply the visibility rules for the page's components, such as personalization or scheduled visibility. + * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-experience?meta=getPage} for more information about the API endpoint. + * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperexperience.shopperexperience-1.html#getpage} for more information on the parameters and returned data type. + * @returns A promise of type Page. + */ +function usePage( + arg: Omit & {rawResponse?: false}, + options?: UseQueryOptions | Response, Error> +): UseQueryResult, Error> +function usePage( + arg: Omit & {rawResponse: true}, + options?: UseQueryOptions | Response, Error> +): UseQueryResult +function usePage( + arg: UsePageArg, + options?: UseQueryOptions | Response, Error> +): UseQueryResult | Response, Error> { + const {headers, rawResponse, ...parameters} = arg + const {locale} = useConfig() + parameters.locale = parameters.locale || locale + return useQuery( + ['/pages', arg], + (_, {shopperExperience}) => { + return shopperExperience.getPage({parameters, headers}, rawResponse) + }, + options + ) +} + +export {usePages, usePage} diff --git a/packages/commerce-sdk-react/src/hooks/ShopperLogin/helper.ts b/packages/commerce-sdk-react/src/hooks/ShopperLogin/helper.ts index 2f5527e7fc..bacbae89bd 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperLogin/helper.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperLogin/helper.ts @@ -4,11 +4,8 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import {ShopperLoginTypes} from 'commerce-sdk-isomorphic' -import {Argument} from '../types' import {useMutation} from '../useMutation' import useAuth from '../useAuth' -import Auth from '../../auth' import {UseMutationResult} from '@tanstack/react-query' export const ShopperLoginHelpers = { @@ -18,7 +15,7 @@ export const ShopperLoginHelpers = { Logout: 'logout' } as const -type ShopperLoginHelpersType = typeof ShopperLoginHelpers[keyof typeof ShopperLoginHelpers] +type ShopperLoginHelpersType = (typeof ShopperLoginHelpers)[keyof typeof ShopperLoginHelpers] /** * A hook for Public Client Shopper Login OAuth helpers. diff --git a/packages/commerce-sdk-react/src/hooks/ShopperOrders/mutation.ts b/packages/commerce-sdk-react/src/hooks/ShopperOrders/mutation.ts index 8a97a8642e..ea52a81bd9 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperOrders/mutation.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperOrders/mutation.ts @@ -49,7 +49,13 @@ export const shopperOrdersCacheUpdateMatrix = { ): CacheUpdateMatrixElement => { const customerId = response?.customerInfo?.customerId return { - update: [{name: 'order', key: ['/orders', {orderNo: response.orderNo}]}], + update: [ + { + name: 'order', + key: ['/orders', {orderNo: response.orderNo}], + updater: () => response + } + ], invalidate: [{name: 'customerBaskets', key: ['/customers', customerId, '/baskets']}] } }, @@ -79,7 +85,8 @@ export const SHOPPER_ORDERS_NOT_IMPLEMENTED = [ 'UpdatePaymentInstrumentForOrder' ] -export type ShopperOrdersMutationType = typeof ShopperOrdersMutations[keyof typeof ShopperOrdersMutations] +export type ShopperOrdersMutationType = + (typeof ShopperOrdersMutations)[keyof typeof ShopperOrdersMutations] type UseShopperOrdersMutationHeaders = NonNullable>['headers'] type UseShopperOrdersMutationArg = { @@ -113,11 +120,13 @@ function useShopperOrdersMutation( return useMutation( (params, apiClients) => { const method = apiClients['shopperOrders'][action] as MutationFunction - return (method.call as ( - apiClient: ShopperOrdersClient, - params: Params, - rawResponse: boolean | undefined - ) => any)(apiClients['shopperOrders'], {...params, headers}, rawResponse) + return ( + method.call as ( + apiClient: ShopperOrdersClient, + params: Params, + rawResponse: boolean | undefined + ) => any + )(apiClients['shopperOrders'], {...params, headers}, rawResponse) }, { onSuccess: (data, params) => { diff --git a/packages/commerce-sdk-react/src/hooks/index.ts b/packages/commerce-sdk-react/src/hooks/index.ts index a1a25cace0..5b68b9b93b 100644 --- a/packages/commerce-sdk-react/src/hooks/index.ts +++ b/packages/commerce-sdk-react/src/hooks/index.ts @@ -7,6 +7,7 @@ export * from './ShopperBaskets' export * from './ShopperContexts' export * from './ShopperCustomers' +export * from './ShopperExperience' export * from './ShopperDiscoverySearch' export * from './ShopperGiftCertificates' export * from './ShopperLogin' diff --git a/packages/commerce-sdk-react/src/hooks/types.ts b/packages/commerce-sdk-react/src/hooks/types.ts index 1455d09c27..2555b43cb0 100644 --- a/packages/commerce-sdk-react/src/hooks/types.ts +++ b/packages/commerce-sdk-react/src/hooks/types.ts @@ -8,6 +8,7 @@ import {ShopperBaskets} from 'commerce-sdk-isomorphic' import {ShopperContexts} from 'commerce-sdk-isomorphic' import {ShopperCustomers} from 'commerce-sdk-isomorphic' import {ShopperDiscoverySearch} from 'commerce-sdk-isomorphic' +import {ShopperExperience} from 'commerce-sdk-isomorphic' import {ShopperGiftCertificates} from 'commerce-sdk-isomorphic' import {ShopperLogin} from 'commerce-sdk-isomorphic' import {ShopperOrders} from 'commerce-sdk-isomorphic' @@ -28,6 +29,7 @@ export interface ApiClients { shopperContexts: ShopperContexts shopperCustomers: ShopperCustomers shopperDiscoverySearch: ShopperDiscoverySearch + shopperExperience: ShopperExperience shopperGiftCertificates: ShopperGiftCertificates shopperLogin: ShopperLogin shopperOrders: ShopperOrders diff --git a/packages/commerce-sdk-react/src/hooks/useCustomerType.ts b/packages/commerce-sdk-react/src/hooks/useCustomerType.ts index 3f9de24acf..d65825dd99 100644 --- a/packages/commerce-sdk-react/src/hooks/useCustomerType.ts +++ b/packages/commerce-sdk-react/src/hooks/useCustomerType.ts @@ -17,12 +17,12 @@ type useCustomerType = { /** * A hook to return customer auth type. - * + * * Customer type can have 3 values: * - null * - guest * - registered - * + * * During initialization, type is null. And it is possible that * isGuest and isRegistered to both be false. * diff --git a/packages/commerce-sdk-react/src/hooks/utils.ts b/packages/commerce-sdk-react/src/hooks/utils.ts index 8f1c79480f..aaa9bfec08 100644 --- a/packages/commerce-sdk-react/src/hooks/utils.ts +++ b/packages/commerce-sdk-react/src/hooks/utils.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import {QueryClient, QueryKey} from '@tanstack/react-query' +import {QueryClient, QueryKey, Updater} from '@tanstack/react-query' import {ApiClients, Argument, DataType} from './types' import {ShopperCustomersMutationType} from './ShopperCustomers' import {ShopperOrdersMutationType} from './ShopperOrders' @@ -13,9 +13,14 @@ import {ShopperBasketsMutationType} from './ShopperBaskets' const isObject = (item: unknown) => typeof item === 'object' && !Array.isArray(item) && item !== null +//TODO: update data type for updater when needed export interface QueryKeyMap { name: string key: QueryKey + updater?: Updater< + DataType | undefined, + DataType | undefined + > } export interface CacheUpdateMatrixElement { @@ -49,18 +54,16 @@ export const updateCache = ( const isMatchingKey = (cacheQuery: {queryKey: {[x: string]: any}}, queryKey: QueryKey) => queryKey.every((item, index) => isObject(item) && isObject(cacheQuery.queryKey[index]) - ? Object.entries(cacheQuery.queryKey[index]) - .sort() - .toString() === + ? Object.entries(cacheQuery.queryKey[index]).sort().toString() === Object.entries(item as Record) .sort() .toString() : item === cacheQuery.queryKey[index] ) - // STEP 1. Update data inside query cache for the matching queryKeys - cacheUpdateMatrix[action]?.(params, response)?.update?.map(({key: queryKey}) => { - queryClient.setQueryData(queryKey, response) + // STEP 1. Update data inside query cache for the matching queryKeys, and updater + cacheUpdateMatrix[action]?.(params, response)?.update?.map(({key: queryKey, updater}) => { + queryClient.setQueryData(queryKey, updater) }) // STEP 2. Invalidate cache entries with the matching queryKeys diff --git a/packages/commerce-sdk-react/src/provider.tsx b/packages/commerce-sdk-react/src/provider.tsx index 17069862e0..495c04982e 100644 --- a/packages/commerce-sdk-react/src/provider.tsx +++ b/packages/commerce-sdk-react/src/provider.tsx @@ -9,6 +9,7 @@ import { ShopperBaskets, ShopperContexts, ShopperCustomers, + ShopperExperience, ShopperLogin, ShopperOrders, ShopperProducts, @@ -86,6 +87,7 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => { shopperContexts: new ShopperContexts(config), shopperCustomers: new ShopperCustomers(config), shopperDiscoverySearch: new ShopperDiscoverySearch(config), + shopperExperience: new ShopperExperience(config), shopperGiftCertificates: new ShopperGiftCertificates(config), shopperLogin: new ShopperLogin(config), shopperOrders: new ShopperOrders(config), diff --git a/packages/commerce-sdk-react/src/test-utils.tsx b/packages/commerce-sdk-react/src/test-utils.tsx index e96990334e..8f150a292f 100644 --- a/packages/commerce-sdk-react/src/test-utils.tsx +++ b/packages/commerce-sdk-react/src/test-utils.tsx @@ -88,29 +88,30 @@ export function renderHookWithProviders( }) } -export const NEW_DATA = {test: 'new data'} -export const OLD_DATA = {test: 'old data'} - -export const mockMutationEndpoints = (matchingPath: string, options?: {errorResponse: number}) => { +export const mockMutationEndpoints = ( + matchingPath: string, + options?: {errorResponse: number}, + response = {} +) => { const responseStatus = options?.errorResponse ? options.errorResponse : 200 nock(DEFAULT_TEST_HOST) .patch((uri) => { return uri.includes(matchingPath) }) - .reply(responseStatus, NEW_DATA) + .reply(responseStatus, response) .put((uri) => { return uri.includes(matchingPath) }) - .reply(responseStatus, NEW_DATA) + .reply(responseStatus, response) .post((uri) => { return uri.includes(matchingPath) }) - .reply(responseStatus, NEW_DATA) + .reply(responseStatus, response) .delete((uri) => { return uri.includes(matchingPath) }) - .reply(responseStatus, NEW_DATA) + .reply(responseStatus, response) } export const assertUpdateQuery = ( diff --git a/packages/commerce-sdk-react/src/utils.ts b/packages/commerce-sdk-react/src/utils.ts new file mode 100644 index 0000000000..ee7d6a60a1 --- /dev/null +++ b/packages/commerce-sdk-react/src/utils.ts @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2023, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +/** + * Utility to determine if you are on the browser (client) or not. + */ +export const onClient = (): boolean => typeof window !== 'undefined' diff --git a/packages/internal-lib-build/.prettierrc.yaml b/packages/internal-lib-build/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/internal-lib-build/.prettierrc.yaml +++ b/packages/internal-lib-build/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/internal-lib-build/package-lock.json b/packages/internal-lib-build/package-lock.json index 8f7ede726d..60c94732e2 100644 --- a/packages/internal-lib-build/package-lock.json +++ b/packages/internal-lib-build/package-lock.json @@ -1,6 +1,6 @@ { "name": "internal-lib-build", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -45,9 +45,9 @@ } }, "@babel/compat-data": { - "version": "7.20.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==" + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==" }, "@babel/core": { "version": "7.20.12", @@ -79,9 +79,9 @@ } }, "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", "requires": { "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", @@ -329,12 +329,12 @@ } }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", "requires": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", + "@babel/traverse": "^7.20.13", "@babel/types": "^7.20.7" } }, @@ -349,9 +349,9 @@ } }, "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==" + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -694,9 +694,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz", - "integrity": "sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw==", + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz", + "integrity": "sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -892,9 +892,9 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz", - "integrity": "sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz", + "integrity": "sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", @@ -999,11 +999,11 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz", - "integrity": "sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.13.tgz", + "integrity": "sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-create-class-features-plugin": "^7.20.12", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-typescript": "^7.20.0" } @@ -1162,26 +1162,26 @@ } }, "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "requires": { "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs2": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.20.7.tgz", - "integrity": "sha512-SrtIxfjwLkUFljufH1GeqYlIYzdyxP2IoCb3tVjcrTdMyB7RQyRCdkyMzvw3k/h+CStnSf2SvvQicS1Rf/fuGQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.20.13.tgz", + "integrity": "sha512-K2yRNithMJG4epI509n4ljPjogMhmYCB887iSD7rRecxWC9dkbfJZCPGj0BQaqG3d3Qkpb1SotEmyeMmtnvxhw==", "requires": { "core-js": "^2.6.12", "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.7.tgz", - "integrity": "sha512-jr9lCZ4RbRQmCR28Q8U8Fu49zvFqLxTY9AMOUz+iyMohMoAgpEcVxY+wJNay99oXOpOcCTODkk70NDN2aaJEeg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.13.tgz", + "integrity": "sha512-p39/6rmY9uvlzRiLZBIB3G9/EBr66LBMcYm7fIDeSBNdRjF2AGD3rFZucUyAgGHC2N+7DdLvVi33uTjSE44FIw==", "requires": { "core-js-pure": "^3.25.1", "regenerator-runtime": "^0.13.11" @@ -1198,9 +1198,9 @@ } }, "@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", "requires": { "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.20.7", @@ -1208,7 +1208,7 @@ "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", + "@babel/parser": "^7.20.13", "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" @@ -1255,9 +1255,9 @@ }, "dependencies": { "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -1278,9 +1278,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -1295,9 +1295,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -1311,9 +1311,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -1328,9 +1328,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -1932,12 +1932,12 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, "@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -2054,14 +2054,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz", - "integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz", + "integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==", "requires": { - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/type-utils": "5.48.2", - "@typescript-eslint/utils": "5.48.2", + "@typescript-eslint/scope-manager": "5.50.0", + "@typescript-eslint/type-utils": "5.50.0", + "@typescript-eslint/utils": "5.50.0", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", @@ -2093,48 +2094,48 @@ } }, "@typescript-eslint/parser": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz", - "integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz", + "integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==", "requires": { - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/typescript-estree": "5.48.2", + "@typescript-eslint/scope-manager": "5.50.0", + "@typescript-eslint/types": "5.50.0", + "@typescript-eslint/typescript-estree": "5.50.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz", - "integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz", + "integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==", "requires": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2" + "@typescript-eslint/types": "5.50.0", + "@typescript-eslint/visitor-keys": "5.50.0" } }, "@typescript-eslint/type-utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz", - "integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz", + "integrity": "sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ==", "requires": { - "@typescript-eslint/typescript-estree": "5.48.2", - "@typescript-eslint/utils": "5.48.2", + "@typescript-eslint/typescript-estree": "5.50.0", + "@typescript-eslint/utils": "5.50.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz", - "integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==" + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz", + "integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==" }, "@typescript-eslint/typescript-estree": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz", - "integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz", + "integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==", "requires": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2", + "@typescript-eslint/types": "5.50.0", + "@typescript-eslint/visitor-keys": "5.50.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2166,15 +2167,15 @@ } }, "@typescript-eslint/utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz", - "integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.50.0.tgz", + "integrity": "sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw==", "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/typescript-estree": "5.48.2", + "@typescript-eslint/scope-manager": "5.50.0", + "@typescript-eslint/types": "5.50.0", + "@typescript-eslint/typescript-estree": "5.50.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -2204,11 +2205,11 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", - "integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==", + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz", + "integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==", "requires": { - "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/types": "5.50.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -2400,6 +2401,18 @@ "es-shim-unscopables": "^1.0.0" } }, + "array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -2431,9 +2444,9 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "axe-core": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.2.tgz", - "integrity": "sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==" + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", + "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" }, "axobject-query": { "version": "2.2.0", @@ -2625,9 +2638,9 @@ }, "dependencies": { "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" } } }, @@ -2810,14 +2823,14 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" } }, "bser": { @@ -2869,9 +2882,9 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caniuse-lite": { - "version": "1.0.30001445", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", - "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==" + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==" }, "capture-exit": { "version": "2.0.0", @@ -3066,17 +3079,17 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.1.tgz", - "integrity": "sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA==", + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", "requires": { "browserslist": "^4.21.4" } }, "core-js-pure": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.27.1.tgz", - "integrity": "sha512-BS2NHgwwUppfeoqOXqi08mUqS5FiZpuRuJJpKsaME7kJz0xxuk0xkhDdfMIlP/zLa80krBqss1LtD7f889heAw==" + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.27.2.tgz", + "integrity": "sha512-Cf2jqAbXgWH3VVzjyaaFkY1EBazxugUepGymDoeteyYr9ByX51kD2jdHZlsEF/xnJMyN3Prua7mQuzwMg6Zc9A==" }, "cross-env": { "version": "5.2.1", @@ -3188,9 +3201,9 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" }, "define-properties": { "version": "1.1.4", @@ -3323,9 +3336,9 @@ } }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + "version": "1.4.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz", + "integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==" }, "emittery": { "version": "0.7.2", @@ -3677,9 +3690,9 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -3947,22 +3960,25 @@ } }, "eslint-plugin-react": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", - "integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" }, "dependencies": { "doctrine": { @@ -3973,6 +3989,11 @@ "esutils": "^2.0.2" } }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, "resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -3982,6 +4003,11 @@ "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -4454,9 +4480,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -4562,6 +4588,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -6513,9 +6544,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "parse5": { "version": "6.0.1", @@ -6859,9 +6890,9 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-fetch": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", - "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "requires": { "whatwg-url": "^5.0.0" }, @@ -6942,9 +6973,9 @@ } }, "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==" + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz", + "integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==" }, "normalize-package-data": { "version": "2.5.0", @@ -7101,6 +7132,15 @@ "es-abstract": "^1.20.4" } }, + "object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -7335,9 +7375,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==" }, "prettier-linter-helpers": { "version": "1.0.0", @@ -7430,9 +7470,9 @@ } }, "punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "querystringify": { "version": "2.2.0", @@ -8700,9 +8740,9 @@ } }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/packages/internal-lib-build/package.json b/packages/internal-lib-build/package.json index 784014bcce..84567f3f8e 100644 --- a/packages/internal-lib-build/package.json +++ b/packages/internal-lib-build/package.json @@ -1,6 +1,6 @@ { "name": "internal-lib-build", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "engines": { "node": "^14.0.0", "npm": "^6.14.4 || ^7.0.0 || ^8.0.0" @@ -56,13 +56,13 @@ "eslint-plugin-import": "2.23.4", "eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-prettier": "3.0.1", - "eslint-plugin-react": "7.24.0", + "eslint-plugin-react": "^7.32.2", "jest": "^26.6.3", "jest-cli": "^26.6.3", "jest-environment-jsdom": "^26.6.2", "jest-environment-jsdom-global": "^2.0.4", "jest-fetch-mock": "^2.1.2", - "prettier": "^1.19.1", + "prettier": "^2.8.3", "raf": "^3.4.0", "regenerator-runtime": "^0.13.9", "replace-in-file": "^6.2.0", diff --git a/packages/pwa-kit-create-app/.prettierrc.yaml b/packages/pwa-kit-create-app/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/pwa-kit-create-app/.prettierrc.yaml +++ b/packages/pwa-kit-create-app/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/pwa-kit-create-app/package-lock.json b/packages/pwa-kit-create-app/package-lock.json index 0473174d27..79b5c58cc9 100644 --- a/packages/pwa-kit-create-app/package-lock.json +++ b/packages/pwa-kit-create-app/package-lock.json @@ -1,9 +1,24 @@ { "name": "pwa-kit-create-app", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@types/lodash": { + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", + "dev": true + }, "@verdaccio/commons-api": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@verdaccio/commons-api/-/commons-api-10.2.0.tgz", @@ -14,54 +29,124 @@ "http-status-codes": "2.2.0" } }, + "@verdaccio/config": { + "version": "6.0.0-6-next.55", + "resolved": "https://registry.npmjs.org/@verdaccio/config/-/config-6.0.0-6-next.55.tgz", + "integrity": "sha512-fZnk9Z4BY5e77F2zZmezfpjdJa65acitOWHI5EUirQ0yaez+eHDLAqHYq4YZ04cBXyk0aF6k9QTXJqj8Y6Luww==", + "dev": true, + "requires": { + "@verdaccio/core": "6.0.0-6-next.55", + "@verdaccio/utils": "6.0.0-6-next.23", + "debug": "4.3.4", + "lodash": "4.17.21", + "minimatch": "3.1.2", + "yaml": "2.2.0", + "yup": "0.32.11" + } + }, + "@verdaccio/core": { + "version": "6.0.0-6-next.55", + "resolved": "https://registry.npmjs.org/@verdaccio/core/-/core-6.0.0-6-next.55.tgz", + "integrity": "sha512-7VNhZw3f92FzUmim3KgQAbG+IuPOtjk9QkeYoI3tvRFmcOVdjg4eh7e6ALu4GiCcTEOpZj6JJMK5PpbflmzMHQ==", + "dev": true, + "requires": { + "ajv": "8.11.2", + "core-js": "3.27.0", + "http-errors": "1.8.1", + "http-status-codes": "2.2.0", + "process-warning": "1.0.0", + "semver": "7.3.8" + }, + "dependencies": { + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + } + } + }, "@verdaccio/file-locking": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-10.2.0.tgz", - "integrity": "sha512-2FR5Tq0xuFLgEIuMPhtdofUk02OiJrBk4bOrQRaIkuYNEqiC0QNzXIz1u8ys2Q++z48affjbJkc9WUnAZRYbJg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@verdaccio/file-locking/-/file-locking-10.3.0.tgz", + "integrity": "sha512-FE5D5H4wy/nhgR/d2J5e1Na9kScj2wMjlLPBHz7XF4XZAVSRdm45+kL3ZmrfA6b2HTADP/uH7H05/cnAYW8bhw==", "dev": true, "requires": { "lockfile": "1.0.4" } }, "@verdaccio/local-storage": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@verdaccio/local-storage/-/local-storage-10.2.1.tgz", - "integrity": "sha512-0ff8TnHvhPu+HSZJvmm8Yb7VRGa/yf7vwpJMQngo2xYg++73CgnUP5hI65NJeKJyg8DX5E0YgCw6HoTbNxBxhg==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@verdaccio/local-storage/-/local-storage-10.3.1.tgz", + "integrity": "sha512-f3oArjXPOAwUAA2dsBhfL/rSouqJ2sfml8k97RtnBPKOzisb28bgyAQW0mqwQvN4MTK5S/2xudmobFpvJAIatg==", "dev": true, "requires": { "@verdaccio/commons-api": "10.2.0", - "@verdaccio/file-locking": "10.2.0", + "@verdaccio/file-locking": "10.3.0", "@verdaccio/streams": "10.2.0", - "async": "3.2.3", + "async": "3.2.4", "debug": "4.3.4", "lodash": "4.17.21", "lowdb": "1.0.0", "mkdirp": "1.0.4" } }, - "@verdaccio/readme": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/@verdaccio/readme/-/readme-10.3.3.tgz", - "integrity": "sha512-VRb9zvs8uXVb5hgSXZ5Ci6meupulFmScd0CJAm+MJeetoSdlr9ERxp3c21hMCct8Djf6gepKOGKItYS6YEDKHA==", - "dev": true, - "requires": { - "dompurify": "2.3.6", - "jsdom": "15.2.1", - "marked": "4.0.14" - } - }, "@verdaccio/streams": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@verdaccio/streams/-/streams-10.2.0.tgz", "integrity": "sha512-FaIzCnDg0x0Js5kSQn1Le3YzDHl7XxrJ0QdIw5LrDUmLsH3VXNi4/NMlSHnw5RiTTMs4UbEf98V3RJRB8exqJA==", "dev": true }, + "@verdaccio/tarball": { + "version": "11.0.0-6-next.24", + "resolved": "https://registry.npmjs.org/@verdaccio/tarball/-/tarball-11.0.0-6-next.24.tgz", + "integrity": "sha512-dcXkLw/b3Elt8xzU0vWc+Yqpb+g6et8vrclgqHhRE0HdArngNMmb8KynvDlkbWpNQJNNdM/bPidCOI3nt7KEnQ==", + "dev": true, + "requires": { + "@verdaccio/core": "6.0.0-6-next.55", + "@verdaccio/url": "11.0.0-6-next.21", + "@verdaccio/utils": "6.0.0-6-next.23", + "debug": "4.3.4", + "lodash": "4.17.21" + } + }, "@verdaccio/ui-theme": { - "version": "6.0.0-6-next.24", - "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-6.0.0-6-next.24.tgz", - "integrity": "sha512-tchic00TMWV9qm3EG1GmU7WLnzb29fGT51NJF8rmmNGc7V7tlpXSOE+WQ/dP99jaViIrZzh73Z03TpjQ3ZFd/A==", + "version": "6.0.0-6-next.55", + "resolved": "https://registry.npmjs.org/@verdaccio/ui-theme/-/ui-theme-6.0.0-6-next.55.tgz", + "integrity": "sha512-EnMYW5vmN8z8gC3WHuS0T/wS+AK+I1/SRmY5IoLUxoaGOAnZmHCeFF7eRIuhRwSQ/qTGCdtvvV6RRT8OaUjSCw==", "dev": true }, + "@verdaccio/url": { + "version": "11.0.0-6-next.21", + "resolved": "https://registry.npmjs.org/@verdaccio/url/-/url-11.0.0-6-next.21.tgz", + "integrity": "sha512-ojDaaDWsq/mvIZu+fxXX+VL/8pEObtAhy0dr5wp3Zo3UrBu4m8ltLqH5RUnj2vUc/5YB/krv/FffgSjWciVPQg==", + "dev": true, + "requires": { + "@verdaccio/core": "6.0.0-6-next.55", + "debug": "4.3.4", + "lodash": "4.17.21", + "validator": "13.7.0" + } + }, + "@verdaccio/utils": { + "version": "6.0.0-6-next.23", + "resolved": "https://registry.npmjs.org/@verdaccio/utils/-/utils-6.0.0-6-next.23.tgz", + "integrity": "sha512-PpKgisv6cRm43JNyFaUTTclyOy8VRubjniA3CmbEQIAImZ1YQMSAQ06nsWpZEhIx4NuRXmB5CiUHg2U4/0+oCg==", + "dev": true, + "requires": { + "@verdaccio/core": "6.0.0-6-next.55", + "lodash": "4.17.21", + "minimatch": "3.1.2", + "semver": "7.3.8" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -72,12 +157,6 @@ "through": ">=2.2.7 <3" } }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -88,36 +167,6 @@ "negotiator": "0.6.3" } }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -128,14 +177,14 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, @@ -161,9 +210,9 @@ } }, "apache-md5": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.7.tgz", - "integrity": "sha512-JtHjzZmJxtzfTSjsCyHgPR155HBe5WGyUyHTaEkfy46qhwCFKx1Epm6nAxgUG3WfUZP1dWhGqj9Z2NOBeZ+uBw==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", + "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", "dev": true }, "argparse": { @@ -172,16 +221,10 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "asn1": { @@ -196,19 +239,19 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "atomic-sleep": { @@ -220,13 +263,13 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true }, "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "balanced-match": { @@ -237,7 +280,7 @@ "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, "requires": { "tweetnacl": "^0.14.3" @@ -246,13 +289,13 @@ "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", "dev": true }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, "requires": { "bytes": "3.1.2", @@ -263,7 +306,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -278,20 +321,17 @@ "ms": "2.0.0" } }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } } } }, @@ -304,16 +344,10 @@ "concat-map": "0.0.1" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, "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": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "dev": true }, "bytes": { @@ -335,7 +369,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, "chalk": { @@ -371,12 +405,12 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" }, "clipanion": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.1.0.tgz", - "integrity": "sha512-v025Hz+IDQ15FpOyK8p02h5bFznMu6rLFsJSyOPR+7WrbSnZ1Ek6pblPukV7K5tC/dsWfncQPIrJ4iUy2PXkbw==", + "version": "3.2.0-rc.14", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.2.0-rc.14.tgz", + "integrity": "sha512-lj5zydbH786t6gpXe6oNX7CM5YKhd0CDhcXG8pKyRa2Nz5cgj1yhnNKxDi/MyPYwjyvAG5oVBeDdYCGUAgD8lQ==", "dev": true, "requires": { - "typanion": "^3.3.1" + "typanion": "^3.8.0" } }, "color-convert": { @@ -433,7 +467,7 @@ "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true }, "debug": { @@ -448,13 +482,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "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==", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -462,7 +490,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "content-disposition": { "version": "0.5.4", @@ -471,24 +499,32 @@ "dev": true, "requires": { "safe-buffer": "5.2.1" + }, + "dependencies": { + "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 + } } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, "cookies": { @@ -499,12 +535,26 @@ "requires": { "depd": "~2.0.0", "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + } } }, + "core-js": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.0.tgz", + "integrity": "sha512-wY6cKosevs430KRkHUIsvepDXHGjlXOZO3hYXNyqpD6JvB0X28aXyv0t1Y1vZMwE7SoKmtfa6IASHCPN52FwBQ==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "cors": { @@ -517,63 +567,19 @@ "vary": "^1" } }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - } - }, "dayjs": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz", - "integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==", + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==", "dev": true }, "debug": { @@ -585,12 +591,6 @@ "ms": "2.1.2" } }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "deepmerge": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", @@ -599,13 +599,13 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, "destroy": { @@ -614,25 +614,10 @@ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "dompurify": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz", - "integrity": "sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==", - "dev": true - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { "jsbn": "~0.1.0", @@ -651,7 +636,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, "emoji-regex": { @@ -662,7 +647,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true }, "envinfo": { @@ -671,185 +656,62 @@ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, - "es5-ext": { - "version": "0.10.61", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", - "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "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": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "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" - } - } - } - }, - "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 - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dev": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", + "send": "0.18.0", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -860,61 +722,27 @@ } }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "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 }, - "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -925,23 +753,6 @@ "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==", "dev": true }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "dev": true, - "requires": { - "type": "^2.5.0" - }, - "dependencies": { - "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", - "dev": true - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -961,7 +772,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, "fast-deep-equal": { @@ -976,16 +787,10 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, "fast-redact": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.1.tgz", - "integrity": "sha512-odVmjC8x8jNeMZ3C+rPMESzXVSEU8tSWSHv9HFxP2mm89G/1WwqhrerJDQm9Zus8X6aoRgQDThKqptdNA6bt+A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", "dev": true }, "fast-safe-stringify": { @@ -1003,17 +808,17 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "dependencies": { @@ -1029,22 +834,13 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } @@ -1058,7 +854,7 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true }, "form-data": { @@ -1081,7 +877,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, "fs-extra": { @@ -1100,12 +896,22 @@ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { "version": "1.1.1", @@ -1113,34 +919,34 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, "requires": { "assert-plus": "^1.0.0" } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -1166,7 +972,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "dev": true }, "har-validator": { @@ -1177,6 +983,26 @@ "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } } }, "has": { @@ -1198,15 +1024,6 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, "http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1218,12 +1035,26 @@ "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } } }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -1238,9 +1069,9 @@ "dev": true }, "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { "agent-base": "6", @@ -1258,7 +1089,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -1294,12 +1125,6 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1307,9 +1132,9 @@ "dev": true }, "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } @@ -1328,13 +1153,13 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "js-yaml": { @@ -1349,42 +1174,8 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", - "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^7.1.0", - "acorn-globals": "^4.3.2", - "array-equal": "^1.0.0", - "cssom": "^0.4.1", - "cssstyle": "^2.0.0", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.1", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.2.0", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "saxes": "^3.1.9", - "symbol-tree": "^3.2.2", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.1.2", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^7.0.0", - "xml-name-validator": "^3.0.0" - } + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true }, "json-schema": { "version": "0.4.0", @@ -1393,15 +1184,15 @@ "dev": true }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "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": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "jsonfile": { @@ -1416,33 +1207,19 @@ "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", "dev": true, "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", + "lodash": "^4.17.21", "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==", - "dev": true - } + "semver": "^7.3.8" } }, "jsprim": { @@ -1488,21 +1265,11 @@ } }, "kleur": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", - "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lockfile": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", @@ -1517,52 +1284,10 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", - "dev": true - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", - "dev": true - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", - "dev": true - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "dev": true }, "lowdb": { @@ -1586,15 +1311,6 @@ "yallist": "^4.0.0" } }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "dev": true, - "requires": { - "es5-ext": "~0.10.2" - } - }, "lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -1610,44 +1326,22 @@ "lunr": ">= 2.3.0 < 2.4.0" } }, - "marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", - "dev": true - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, - "memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "mime": { @@ -1685,18 +1379,15 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "requires": { - "yallist": "^4.0.0" - } + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.1.tgz", + "integrity": "sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==" }, "minizlib": { "version": "2.1.2", @@ -1705,6 +1396,16 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "mkdirp": { @@ -1726,7 +1427,7 @@ "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", "dev": true, "requires": { "mkdirp": "~0.5.1", @@ -1745,10 +1446,16 @@ } } }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==", + "dev": true + }, "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "dev": true }, "negotiator": { @@ -1763,51 +1470,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", "dev": true, "requires": { "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } } }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -1817,13 +1488,13 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "on-finished": { @@ -1844,7 +1515,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -1857,24 +1528,10 @@ "mimic-fn": "^2.1.0" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, "parse-ms": { "version": "2.1.0", @@ -1882,12 +1539,6 @@ "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", "dev": true }, - "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", - "dev": true - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1897,7 +1548,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-parse": { "version": "1.0.7", @@ -1907,19 +1558,19 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true }, "pino": { @@ -1946,25 +1597,13 @@ "pkginfo": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", - "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", - "dev": true - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==", "dev": true }, "prettier-bytes": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prettier-bytes/-/prettier-bytes-1.0.4.tgz", - "integrity": "sha1-mUsCqkb2mcULYle1+qp/4lV+YtY=", + "integrity": "sha512-dLbWOa4xBn+qeWeIF60qRoB6Pk2jX5P3DIVgOQyMyvBpu931Q+8dXz8X0snJiFkQdohDDLnZQECjzsAj75hgZQ==", "dev": true }, "pretty-ms": { @@ -1982,6 +1621,12 @@ "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==", "dev": true }, + "property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1993,22 +1638,25 @@ } }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true + "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" + } }, "quick-format-unescaped": { "version": "4.0.4", @@ -2037,11 +1685,17 @@ "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "requires": { "resolve": "^1.1.6" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -2070,62 +1724,26 @@ "uuid": "^3.3.2" }, "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } } } }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "dev": true, - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -2142,7 +1760,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", "dev": true, "requires": { "glob": "^6.0.1" @@ -2151,7 +1769,7 @@ "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", "dev": true, "requires": { "inflight": "^1.0.4", @@ -2177,9 +1795,9 @@ } }, "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==", + "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 }, "safer-buffer": { @@ -2187,42 +1805,33 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "saxes": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", - "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", - "dev": true, - "requires": { - "xmlchars": "^2.1.1" - } - }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } }, "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "debug": { @@ -2237,36 +1846,17 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2279,33 +1869,24 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true } } }, "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.2" + "send": "0.18.0" } }, "setprototypeof": { @@ -2379,21 +1960,15 @@ } }, "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true }, "steno": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", - "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "integrity": "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==", "dev": true, "requires": { "graceful-fs": "^4.1.3" @@ -2430,20 +2005,14 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -2452,17 +2021,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dev": true, - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "tmp": { "version": "0.0.33", @@ -2478,25 +2037,35 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true + }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + } } }, "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "tslib": { "version": "1.14.1", @@ -2512,7 +2081,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -2521,30 +2090,15 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "typanion": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.8.0.tgz", - "integrity": "sha512-r9rEMpvF4pnu2DuYuC//ctH7I83bdx+Psvi1GO68w4942OmFiH56+5YS9vsEe2+9ipFPOnBHW0Z2z5/nGz5jTg==", - "dev": true - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.12.1.tgz", + "integrity": "sha512-3SJF/czpzqq6G3lprGFLa6ps12yb1uQ1EmitNnep2fDMNh1aO/Zbq9sWY+3lem0zYb2oHJnQWyabTGUZ+L1ScQ==", "dev": true }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -2561,9 +2115,9 @@ } }, "uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true }, @@ -2581,7 +2135,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, "uri-js": { @@ -2596,7 +2150,7 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "uuid": { @@ -2614,46 +2168,45 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, "verdaccio": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-5.10.0.tgz", - "integrity": "sha512-K2bHpRfOX1l2vKgwVdVqat25wDqv4ytQoA2fuBO5+vaGfRb+CLdv9H8JVft2b7GBjARpPXkFEek/dJfSZd7E5A==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-5.20.1.tgz", + "integrity": "sha512-zKQXYubQOfl2w09gO9BR7U9ZZkFPPby8tvV+na86/2vGZnY79kNSVnSbK8CM1bpJHTCQ80AGsmIGovg2FgXhdQ==", "dev": true, "requires": { - "@verdaccio/commons-api": "10.2.0", - "@verdaccio/local-storage": "10.2.1", - "@verdaccio/readme": "10.3.3", + "@verdaccio/config": "6.0.0-6-next.55", + "@verdaccio/core": "6.0.0-6-next.55", + "@verdaccio/local-storage": "10.3.1", "@verdaccio/streams": "10.2.0", - "@verdaccio/ui-theme": "6.0.0-6-next.24", + "@verdaccio/tarball": "11.0.0-6-next.24", + "@verdaccio/ui-theme": "6.0.0-6-next.55", + "@verdaccio/url": "11.0.0-6-next.21", + "@verdaccio/utils": "6.0.0-6-next.23", "JSONStream": "1.3.5", - "async": "3.2.3", - "body-parser": "1.20.0", - "clipanion": "3.1.0", + "async": "3.2.4", + "body-parser": "1.20.1", + "clipanion": "3.2.0-rc.14", "compression": "1.7.4", "cookies": "0.8.0", "cors": "2.8.5", - "dayjs": "1.11.1", - "debug": "^4.3.3", + "dayjs": "1.11.7", + "debug": "^4.3.4", "envinfo": "7.8.1", - "eslint-import-resolver-node": "0.3.6", - "express": "4.17.3", + "express": "4.18.2", "express-rate-limit": "5.5.1", "fast-safe-stringify": "2.1.1", "handlebars": "4.7.7", "http-errors": "2.0.0", "js-yaml": "4.1.0", - "jsonwebtoken": "8.5.1", - "kleur": "4.1.4", + "jsonwebtoken": "9.0.0", + "kleur": "4.1.5", "lodash": "4.17.21", - "lru-cache": "7.8.1", + "lru-cache": "7.14.1", "lunr-mutable-indexes": "2.3.2", - "marked": "4.0.14", - "memoizee": "0.4.15", "mime": "3.0.0", - "minimatch": "5.0.1", "mkdirp": "1.0.4", "mv": "2.1.1", "pino": "6.14.0", @@ -2661,58 +2214,40 @@ "prettier-bytes": "^1.0.4", "pretty-ms": "^7.0.1", "request": "2.88.0", - "semver": "7.3.7", + "semver": "7.3.8", "validator": "13.7.0", - "verdaccio-audit": "10.2.1", - "verdaccio-htpasswd": "10.3.0" + "verdaccio-audit": "10.2.4", + "verdaccio-htpasswd": "10.5.2" }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, "lru-cache": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz", - "integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", + "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", "dev": true - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } } } }, "verdaccio-audit": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-10.2.1.tgz", - "integrity": "sha512-zDG0Kw1ny+Kj+k134/gVN5B3/+8h7i8dKdw4wqVf8CcaYfXlIAIgdwPB1DeD/D2DFSy43FokSO9erTKPHGHidw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/verdaccio-audit/-/verdaccio-audit-10.2.4.tgz", + "integrity": "sha512-/0H6/JFVnhHwucUfMRVjL6gtGnB5gr3dDxq93Ja1Y0ob+2jxAfpqNMHg8c6/d/ZyHFf0y4tXzHESDruXCzTiaQ==", "dev": true, "requires": { - "body-parser": "1.20.0", - "express": "4.17.3", - "https-proxy-agent": "5.0.0", - "node-fetch": "2.6.7" + "body-parser": "1.20.1", + "express": "4.18.2", + "https-proxy-agent": "5.0.1", + "node-fetch": "2.6.8" } }, "verdaccio-htpasswd": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-10.3.0.tgz", - "integrity": "sha512-UbMF9kbqo2tvOrdbC3MryE6/iXy54XlqDKpFWUKS5MTjFhP9BdQNgyTjBCM/mubO3JJug2TcVdmu/si8G4891Q==", + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/verdaccio-htpasswd/-/verdaccio-htpasswd-10.5.2.tgz", + "integrity": "sha512-bO5Wm8w07pWswNvwFWjNEoznuUU37CcfblcrU0Ci8c038EgTu2V47uwh4AyZ4PTK6ps9oxHqA7a1b+83sY0OkA==", "dev": true, "requires": { - "@verdaccio/file-locking": "10.2.0", - "apache-md5": "1.1.7", + "@verdaccio/file-locking": "10.3.0", + "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "http-errors": "2.0.0", "unix-crypt-td-js": "1.1.4" @@ -2721,7 +2256,7 @@ "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -2729,97 +2264,58 @@ "extsprintf": "^1.2.0" } }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", - "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", - "dev": true, - "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", - "xml-name-validator": "^3.0.0" - } - }, "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.0.tgz", + "integrity": "sha512-auf7Gi6QwO7HW//GA9seGvTXVGWl1CM/ADWh1+RxtXr6XOxnT65ovDl9fTi4e0monEyJxCHqDpF6QnFDXmJE4g==", + "dev": true + }, + "yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } } } } diff --git a/packages/pwa-kit-create-app/package.json b/packages/pwa-kit-create-app/package.json index dfbccaa0ff..f63aa17c92 100644 --- a/packages/pwa-kit-create-app/package.json +++ b/packages/pwa-kit-create-app/package.json @@ -1,6 +1,6 @@ { "name": "pwa-kit-create-app", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "description": "Salesforce's project generator tool", "author": "cc-pwa-kit@salesforce.com", "license": "See license in LICENSE", @@ -40,7 +40,7 @@ "tar": "^6.1.11" }, "devDependencies": { - "internal-lib-build": "^2.6.0-dev", + "internal-lib-build": "^2.7.0-dev", "verdaccio": "^5.9.0" } } diff --git a/packages/pwa-kit-dev/.prettierrc.yaml b/packages/pwa-kit-dev/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/pwa-kit-dev/.prettierrc.yaml +++ b/packages/pwa-kit-dev/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/pwa-kit-dev/CHANGELOG.md b/packages/pwa-kit-dev/CHANGELOG.md index 201537cad1..13f15ccd76 100644 --- a/packages/pwa-kit-dev/CHANGELOG.md +++ b/packages/pwa-kit-dev/CHANGELOG.md @@ -1,4 +1,8 @@ -## v2.6.0-dev (Jan 05, 2023) +## v2.7.0-dev (Jan 25, 2023) +## v2.6.0 (Jan 25, 2023) +- Upgrade prettier to v2 [#926](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/926) +- Security package updates + ## v2.5.0 (Jan 05, 2023) - Logging cid from res header isntead of req in local development [#821](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/821) diff --git a/packages/pwa-kit-dev/bin/pwa-kit-dev.js b/packages/pwa-kit-dev/bin/pwa-kit-dev.js index 590b4c439e..d4c0ba365e 100755 --- a/packages/pwa-kit-dev/bin/pwa-kit-dev.js +++ b/packages/pwa-kit-dev/bin/pwa-kit-dev.js @@ -12,23 +12,53 @@ const WebSocket = require('ws') const program = require('commander') const validator = require('validator') const {execSync: _execSync} = require('child_process') -const scriptUtils = require('../scripts/utils') -const uploadBundle = require('../scripts/upload.js') -const pkg = require('../package.json') const {getConfig} = require('pwa-kit-runtime/utils/ssr-config') +// Scripts in ./bin have never gone through babel, so we +// don't have a good pattern for mixing compiled/un-compiled +// code. +// +// This conditional import lets us gradually migrate portions +// of this script to Typescript, until internal-lib-build +// has a decent pattern for ./bin scripts! +const scriptUtils = (() => { + try { + return require('../dist/utils/script-utils') + } catch { + return require('../utils/script-utils') + } +})() + const colors = { - info: 'green', warn: 'yellow', - error: 'red' + error: 'red', + success: 'cyan' +} + +const fancyLog = (level, msg) => { + const color = colors[level] || 'green' + const colorFn = chalk[color] + console.log(`${colorFn(level)}: ${msg}`) } +const info = (msg) => fancyLog('info', msg) +const success = (msg) => fancyLog('success', msg) +const warn = (msg) => fancyLog('warn', msg) +const error = (msg) => fancyLog('error', msg) const execSync = (cmd, opts) => { const defaults = {stdio: 'inherit'} return _execSync(cmd, {...defaults, ...opts}) } -const main = () => { +const getProjectName = async () => { + const projectPkg = await scriptUtils.getProjectPkg() + if (!projectPkg.name) { + throw new Error(`Missing "name" field in "package.json"`) + } + return projectPkg.name +} + +const main = async () => { const pkgRoot = p.join(__dirname, '..') process.env.CONTEXT = process.cwd() @@ -48,7 +78,7 @@ const main = () => { ``, `Usage inside NPM scripts:`, ``, - ` The PWA Kit Developer Tools is used in NPM scripts so you can conveniently`, + ` The PWA Kit Developer Tools are used in NPM scripts so you can conveniently`, ` run eg. 'npm run push' to push a bundle from a project.`, ``, ` To pass args to pwa-kit-dev when wrapped in an NPM script, separate them`, @@ -65,6 +95,19 @@ const main = () => { ].join('\n') ) + /** + * Return a platform-specific representation of the default credentials + * location *for documentation purposes only*. + * + * It's easier to recognize the intention behind `(default "~/.mobify")` in + * docs than it is `(default "/Users/xyz/.mobify")`. In the second case, + * you have to actually remember that this is your home dir! + */ + const credentialsLocationDisplay = () => { + const dir = process.platform === 'win32' ? '%USERPROFILE%' : '~' + return p.join(dir, '.mobify') + } + /** * All Managed Runtime commands take common opts like --cloud-origin * and --credentialsFile. These are set to be split out from the SDK @@ -75,36 +118,28 @@ const main = () => { .command(name) .addOption( new program.Option('--cloud-origin ', 'the API origin to connect to') - .default('https://cloud.mobify.com') + .default(scriptUtils.DEFAULT_CLOUD_ORIGIN) .env('CLOUD_API_BASE') - .argParser((val) => { - try { - const url = new URL(val) - const labels = url.host.split('.') - if ( - labels.length !== 3 || - !labels[0].startsWith('cloud') || - !labels[1].startsWith('mobify') || - labels[2] !== 'com' - ) { - throw new Error() - } - } catch { - throw new program.InvalidArgumentError( - `'${val}' is not a valid Cloud origin` - ) - } - return val - }) ) .addOption( new program.Option( '-c, --credentialsFile ', - 'override the standard credentials file location' + `override the standard credentials file location "${credentialsLocationDisplay()}"` ) - .default(scriptUtils.getCredentialsFile()) + // Must default to undefined in order to trigger automatic-lookup + // of a credentials file, based on --cloud-origin. + .default(undefined) .env('PWA_KIT_CREDENTIALS_FILE') ) + .hook('preAction', (thisCommand, actionCommand) => { + // The final credentialsFile path depends on both cloudOrigin and credentialsFile opts. + // Pre-process before passing to the command. + const {cloudOrigin, credentialsFile} = actionCommand.opts() + actionCommand.setOptionValue( + 'credentialsFile', + scriptUtils.getCredentialsFile(cloudOrigin, credentialsFile) + ) + }) } managedRuntimeCommand('save-credentials') @@ -124,21 +159,20 @@ const main = () => { '-k, --key ', `find your API key at https://runtime.commercecloud.com/account/settings`, (val) => { - if (!(typeof val === 'string' && val.length > 0)) { - throw new program.InvalidArgumentError(`"${val}" cannot be empty`) + if (typeof val !== 'string' || val === '') { + throw new program.InvalidArgumentError(`"api-key" cannot be empty`) } else { return val } } ) - .action(({user, key, credentialsFile}) => { + .action(async ({user, key, credentialsFile}) => { try { fse.writeJson(credentialsFile, {username: user, api_key: key}, {spaces: 4}) - console.log(`Saved Managed Runtime credentials to "${credentialsFile}".`) + success(`Saved Managed Runtime credentials to "${chalk.cyan(credentialsFile)}".`) } catch (e) { - console.error('Failed to save credentials.') - console.error(e) - process.exit(1) + error('Failed to save credentials.') + throw e } }) @@ -149,7 +183,7 @@ const main = () => { new program.Option('--inspect', 'enable debugging with --inspect on the node process') ) .addOption(new program.Option('--noHMR', 'disable the client-side hot module replacement')) - .action(({inspect, noHMR}) => { + .action(async ({inspect, noHMR}) => { execSync( `node${inspect ? ' --inspect' : ''} ${p.join(process.cwd(), 'app', 'ssr.js')}`, { @@ -172,7 +206,7 @@ const main = () => { .env('PWA_KIT_BUILD_DIR') ) .description(`build your app for production`) - .action(({buildDirectory}) => { + .action(async ({buildDirectory}) => { const webpack = p.join(require.resolve('webpack'), '..', '..', '..', '.bin', 'webpack') const projectWebpack = p.join(process.cwd(), 'webpack.config.js') const webpackConf = fse.pathExistsSync(projectWebpack) @@ -241,50 +275,54 @@ const main = () => { 'immediately deploy the bundle to this target once it is pushed' ) ) - .action(({buildDirectory, message, projectSlug, target, cloudOrigin, credentialsFile}) => { - // Set the deployment target env var, this is required to ensure we - // get the correct configuration object. - process.env.DEPLOY_TARGET = target - const mobify = getConfig() || {} - - if (!projectSlug) { - projectSlug = scriptUtils.readPackageJson('name') - } - - const options = { + .action( + async ({ buildDirectory, - // Avoid setting message if it's blank, so that it doesn't override the default - ...(message ? {message} : undefined), + message, projectSlug, target, - credentialsFile, - // Note: Cloud expects snake_case, but package.json uses camelCase. - ssr_parameters: mobify.ssrParameters, - ssr_only: mobify.ssrOnly, - ssr_shared: mobify.ssrShared, - set_ssr_values: true, - origin: cloudOrigin - } + cloudOrigin, + credentialsFile + }) => { + // Set the deployment target env var, this is required to ensure we + // get the correct configuration object. + process.env.DEPLOY_TARGET = target + + const credentials = await scriptUtils.readCredentials(credentialsFile) + + const mobify = getConfig() || {} + + if (!projectSlug) { + projectSlug = await getProjectName() + } + + const bundle = await scriptUtils.createBundle({ + message, + ssr_parameters: mobify.ssrParameters, + ssr_only: mobify.ssrOnly, + ssr_shared: mobify.ssrShared, + buildDirectory, + projectSlug + }) + const client = new scriptUtils.CloudAPIClient({ + credentials, + origin: cloudOrigin + }) - if ( - !Array.isArray(options.ssr_only) || - options.ssr_only.length === 0 || - !Array.isArray(options.ssr_shared) || - options.ssr_shared.length === 0 - ) { - scriptUtils.fail('ssrEnabled is set, but no ssrOnly or ssrShared files are defined') + info(`Beginning upload to ${cloudOrigin}`) + const data = await client.push(bundle, projectSlug, target) + const warnings = data.warnings || [] + warnings.forEach(warn) + success('Bundle Uploaded') } - uploadBundle(options).catch((err) => { - console.error(err.message || err) - }) - }) + ) program .command('lint') .description('lint all source files') .argument('', 'path or glob to lint') .option('--fix', 'Try and fix errors (default: false)') - .action((path, {fix}) => { + .action(async (path, {fix}) => { const eslint = p.join(require.resolve('eslint'), '..', '..', '..', '.bin', 'eslint') const eslintConfig = p.join(__dirname, '..', 'configs', 'eslint', 'eslint-config.js') execSync( @@ -298,7 +336,7 @@ const main = () => { .command('format') .description('automatically re-format all source files') .argument('', 'path or glob to format') - .action((path) => { + .action(async (path) => { const prettier = p.join(require.resolve('prettier'), '..', '..', '.bin', 'prettier') execSync(`${prettier} --write "${path}"`) }) @@ -306,7 +344,7 @@ const main = () => { program .command('test') .description('test the project') - .action((_, {args}) => { + .action(async (_, {args}) => { const jest = p.join(require.resolve('jest'), '..', '..', '..', '.bin', 'jest') execSync( `${jest} --passWithNoTests --maxWorkers=2${args.length ? ' ' + args.join(' ') : ''}` @@ -322,24 +360,20 @@ const main = () => { ) ) .requiredOption('-e, --environment ', 'the environment slug') - .action(async ({project, environment, cloudOrigin, credentialsFile}, command) => { + .action(async ({project, environment, cloudOrigin, credentialsFile}) => { if (!project) { - project = scriptUtils.readPackageJson('name') + project = await getProjectName() } - let credentials - try { - credentials = fse.readJsonSync(credentialsFile) - } catch (e) { - scriptUtils.fail(`Error reading credentials: ${e}`) - } + const credentials = await scriptUtils.readCredentials(credentialsFile) + + const client = new scriptUtils.CloudAPIClient({ + credentials, + origin: cloudOrigin + }) + + const token = await client.createLoggingToken(project, environment) - const token = await scriptUtils.createToken( - project, - environment, - cloudOrigin, - credentials.api_key - ) const url = new URL(cloudOrigin.replace('cloud', 'logs')) url.protocol = 'wss' url.search = new URLSearchParams({ @@ -363,9 +397,10 @@ const main = () => { console.log('Connection closed with code', code) }) - ws.on('error', (error) => { + ws.on('error', (err) => { clearInterval(heartbeat) - scriptUtils.fail(`Error tailing logs: ${error.message}`) + error(`Error tailing logs: ${err.message}`) + throw err }) ws.on('message', (data) => { @@ -386,21 +421,23 @@ const main = () => { }) // Global options - - program.option('-v, --version', 'show version number').action(({version}) => { + program.option('-v, --version', 'show version number').action(async ({version}) => { if (version) { + const pkg = await scriptUtils.getPkgJSON() console.log(pkg.version) } else { program.help({error: true}) } }) - program.parse(process.argv) + await program.parseAsync(process.argv) } -Promise.resolve() - .then(() => main()) - .catch((err) => { - console.error(err.message) +Promise.resolve().then(async () => { + try { + await main() + } catch (err) { + error(err.message || err.toString()) process.exit(1) - }) + } +}) diff --git a/packages/pwa-kit-dev/package-lock.json b/packages/pwa-kit-dev/package-lock.json index b52a554f53..36c16e905c 100644 --- a/packages/pwa-kit-dev/package-lock.json +++ b/packages/pwa-kit-dev/package-lock.json @@ -1,6 +1,6 @@ { "name": "pwa-kit-dev", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -66,9 +66,9 @@ } }, "@babel/compat-data": { - "version": "7.20.10", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", - "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==" + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==" }, "@babel/core": { "version": "7.20.12", @@ -100,9 +100,9 @@ } }, "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", "requires": { "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", @@ -350,12 +350,12 @@ } }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", "requires": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", + "@babel/traverse": "^7.20.13", "@babel/types": "^7.20.7" } }, @@ -382,9 +382,9 @@ } }, "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==" + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -727,9 +727,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz", - "integrity": "sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw==", + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz", + "integrity": "sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==", "requires": { "@babel/helper-plugin-utils": "^7.20.2" } @@ -925,9 +925,9 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz", - "integrity": "sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz", + "integrity": "sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", @@ -1032,11 +1032,11 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz", - "integrity": "sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.13.tgz", + "integrity": "sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-create-class-features-plugin": "^7.20.12", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-typescript": "^7.20.0" } @@ -1195,26 +1195,26 @@ } }, "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "requires": { "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs2": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.20.7.tgz", - "integrity": "sha512-SrtIxfjwLkUFljufH1GeqYlIYzdyxP2IoCb3tVjcrTdMyB7RQyRCdkyMzvw3k/h+CStnSf2SvvQicS1Rf/fuGQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.20.13.tgz", + "integrity": "sha512-K2yRNithMJG4epI509n4ljPjogMhmYCB887iSD7rRecxWC9dkbfJZCPGj0BQaqG3d3Qkpb1SotEmyeMmtnvxhw==", "requires": { "core-js": "^2.6.12", "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.7.tgz", - "integrity": "sha512-jr9lCZ4RbRQmCR28Q8U8Fu49zvFqLxTY9AMOUz+iyMohMoAgpEcVxY+wJNay99oXOpOcCTODkk70NDN2aaJEeg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.13.tgz", + "integrity": "sha512-p39/6rmY9uvlzRiLZBIB3G9/EBr66LBMcYm7fIDeSBNdRjF2AGD3rFZucUyAgGHC2N+7DdLvVi33uTjSE44FIw==", "requires": { "core-js-pure": "^3.25.1", "regenerator-runtime": "^0.13.11" @@ -1231,9 +1231,9 @@ } }, "@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", "requires": { "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.20.7", @@ -1241,7 +1241,7 @@ "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", + "@babel/parser": "^7.20.13", "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" @@ -1284,9 +1284,9 @@ }, "dependencies": { "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -1729,17 +1729,17 @@ } }, "@loadable/babel-plugin": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@loadable/babel-plugin/-/babel-plugin-5.13.2.tgz", - "integrity": "sha512-vSZUVeTH1S1sDbk8Tzft0plZSkN7W4zmVR5w/Bmy4UmvBiu9lin7ztrDpoUTUzxpoups+OJbTc/OosvN0aMXWg==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/babel-plugin/-/babel-plugin-5.15.3.tgz", + "integrity": "sha512-kwEsPxCk8vnwbTfbA4lHqT5t0u0czCQTnCcmOaTjxT5lCn7yZCBTBa9D7lHs+MLM2WyPsZlee3Qh0TTkMMi5jg==", "requires": { "@babel/plugin-syntax-dynamic-import": "^7.7.4" } }, "@loadable/component": { - "version": "5.15.2", - "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", - "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.3.tgz", + "integrity": "sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==", "dev": true, "requires": { "@babel/runtime": "^7.7.7", @@ -1748,9 +1748,9 @@ } }, "@loadable/server": { - "version": "5.15.2", - "resolved": "https://registry.npmjs.org/@loadable/server/-/server-5.15.2.tgz", - "integrity": "sha512-Nk/6Plz8qTn+A+u2qg/vrbYZwTmzzjy5bw4Ts80pM/rqW8H37IooKU/HVWSje63RZ4vTqEsHsrdiX97RB9dMjw==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/server/-/server-5.15.3.tgz", + "integrity": "sha512-Bm/BGe+RlChuHDKNNXpQOi4AJ0cKVuSLI+J8U0Q06zTIfT0S1RLoy85qs5RXm3cLIfefygL8+9bcYFgeWcoM8A==", "requires": { "lodash": "^4.17.15" } @@ -1895,12 +1895,12 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, "@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -1940,9 +1940,9 @@ } }, "@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.0.tgz", + "integrity": "sha512-35EhHNOXgxnUgh4XCJsGhE7zdlDhYDN/aMG6UbkByCFFNgQ7b3U+uVoqBpicFydR8JEfgdjCF7SJ7MiJfzuiTA==", "requires": { "@types/estree": "*", "@types/json-schema": "*" @@ -2468,6 +2468,18 @@ "es-shim-unscopables": "^1.0.0" } }, + "array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -2530,9 +2542,9 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, "axe-core": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.2.tgz", - "integrity": "sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==" + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", + "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" }, "axobject-query": { "version": "2.2.0", @@ -2923,14 +2935,14 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" } }, "bser": { @@ -3020,9 +3032,9 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caniuse-lite": { - "version": "1.0.30001445", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", - "integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==" + "version": "1.0.30001450", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", + "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==" }, "capture-exit": { "version": "2.0.0", @@ -3308,9 +3320,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.9.0", @@ -3367,17 +3379,17 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-compat": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.1.tgz", - "integrity": "sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA==", + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", "requires": { "browserslist": "^4.21.4" } }, "core-js-pure": { - "version": "3.27.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.27.1.tgz", - "integrity": "sha512-BS2NHgwwUppfeoqOXqi08mUqS5FiZpuRuJJpKsaME7kJz0xxuk0xkhDdfMIlP/zLa80krBqss1LtD7f889heAw==" + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.27.2.tgz", + "integrity": "sha512-Cf2jqAbXgWH3VVzjyaaFkY1EBazxugUepGymDoeteyYr9ByX51kD2jdHZlsEF/xnJMyN3Prua7mQuzwMg6Zc9A==" }, "core-util-is": { "version": "1.0.3", @@ -3502,9 +3514,9 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" }, "define-lazy-prop": { "version": "2.0.0", @@ -3681,9 +3693,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + "version": "1.4.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz", + "integrity": "sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==" }, "emittery": { "version": "0.7.2", @@ -3975,9 +3987,9 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "requires": { "type-fest": "^0.20.2" } @@ -4232,22 +4244,25 @@ } }, "eslint-plugin-react": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", - "integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" }, "dependencies": { "doctrine": { @@ -4258,6 +4273,19 @@ "esutils": "^2.0.2" } }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -4267,6 +4295,11 @@ "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -4905,9 +4938,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -6506,9 +6539,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" } } }, @@ -6927,9 +6960,9 @@ } }, "node-fetch": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", - "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", "requires": { "whatwg-url": "^5.0.0" }, @@ -7010,9 +7043,9 @@ } }, "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==" + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz", + "integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==" }, "normalize-package-data": { "version": "2.5.0", @@ -7130,6 +7163,15 @@ "es-abstract": "^1.20.4" } }, + "object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -7515,9 +7557,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==" }, "prettier-linter-helpers": { "version": "1.0.0", @@ -7630,9 +7672,9 @@ } }, "punycode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", - "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "qs": { "version": "6.11.0", @@ -9329,9 +9371,9 @@ } }, "terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.16.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz", + "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -9340,9 +9382,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "commander": { "version": "2.20.3", @@ -9548,9 +9590,9 @@ } }, "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "tunnel-agent": { "version": "0.6.0", @@ -9611,9 +9653,9 @@ } }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "unbox-primitive": { "version": "1.0.2", @@ -9803,9 +9845,9 @@ } }, "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" }, "vary": { "version": "1.1.2", @@ -9923,9 +9965,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "enhanced-resolve": { "version": "5.12.0", @@ -9960,9 +10002,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" }, "acorn-walk": { "version": "8.2.0", diff --git a/packages/pwa-kit-dev/package.json b/packages/pwa-kit-dev/package.json index c733c95c0e..e29d7367c2 100644 --- a/packages/pwa-kit-dev/package.json +++ b/packages/pwa-kit-dev/package.json @@ -1,6 +1,6 @@ { "name": "pwa-kit-dev", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "description": "Build tools for pwa-kit", "repository": { "type": "git", @@ -72,7 +72,7 @@ "eslint-plugin-import": "2.23.4", "eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-prettier": "3.0.1", - "eslint-plugin-react": "7.24.0", + "eslint-plugin-react": "^7.32.2", "express": "^4.17.1", "fs-extra": "^10.1.0", "git-rev-sync": "^3.0.1", @@ -87,8 +87,8 @@ "mime-types": "2.1.32", "minimatch": "3.0.5", "open": "^8.4.0", - "prettier": "^1.18.2", - "pwa-kit-runtime": "^2.6.0-dev", + "prettier": "^2.8.3", + "pwa-kit-runtime": "^2.7.0-dev", "react-refresh": "^0.13.0", "replace-in-file": "^6.2.0", "request": "^2.88.0", @@ -108,7 +108,7 @@ }, "devDependencies": { "@loadable/component": "^5.15.0", - "internal-lib-build": "^2.6.0-dev", + "internal-lib-build": "^2.7.0-dev", "nock": "^13.1.1", "superagent": "^6.1.0", "supertest": "^4.0.2" diff --git a/packages/pwa-kit-dev/scripts/build-request.js b/packages/pwa-kit-dev/scripts/build-request.js deleted file mode 100644 index fb7143983b..0000000000 --- a/packages/pwa-kit-dev/scripts/build-request.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -'use strict' - -const path = require('path') -const request = require('request') -const Utils = require('./utils') -const URL = require('url').URL - -const buildRequest = (options, dataBuffer) => { - // e.g. https://cloud.mobify.com/api/projects/progressive-web/builds/ - /* eslint-disable prefer-template */ - const pathname = path.posix.join( - 'api', - 'projects', - options.projectSlug, - 'builds', - options.target || '', - '/' - ) - const uri = new URL(pathname, options.origin) - - /* eslint-enable prefer-template */ - const headers = Utils.getRequestHeaders({'Content-Length': options.dataLength}) - - const requestOptions = { - uri, - method: 'POST', - auth: { - user: options.username, - pass: options.api_key - }, - headers - } - - return new Promise((resolve) => { - request(requestOptions, (error, response, body) => { - if (error || (error = Utils.errorForStatus(response))) { - let message - if (error.code === 'ECONNREFUSED') { - message = - `Unable to connect to ${options.origin}.` + - `Check your connection and try again.\n` + - `For more information visit ${Utils.DEFAULT_DOCS_URL}'.` - } else { - message = error.message - } - - Utils.fail(message) - } - - resolve(body) - }).end(dataBuffer) - }) - .then((body) => JSON.parse(body)) - .catch((e) => Utils.fail(e)) -} - -module.exports = buildRequest diff --git a/packages/pwa-kit-dev/scripts/build-request.test.js b/packages/pwa-kit-dev/scripts/build-request.test.js deleted file mode 100644 index e004ed63e0..0000000000 --- a/packages/pwa-kit-dev/scripts/build-request.test.js +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -jest.mock('request') -jest.mock('./utils') - -const Request = require('request') -const Utils = require('./utils') - -const buildRequest = require('./build-request') - -test('correctly makes a request', () => { - Request.mockClear() - Request.mockImplementation((options, callback) => { - callback(null, {}, '') - }) - - Utils.getRequestHeaders.mockClear() - const headerMock = {test: true} - Utils.getRequestHeaders.mockReturnValueOnce(headerMock) - - return buildRequest( - { - origin: 'https://test.mobify.com', - projectSlug: 'progressive-web-sdk-tests', - dataLength: 12345, - username: 'space-whales', - api_key: 'KEY' - }, - {} - ).then(() => { - expect(Request).toBeCalled() - const options = Request.mock.calls[0][0] - expect(options.uri.toString()).toBe( - 'https://test.mobify.com/api/projects/progressive-web-sdk-tests/builds/' - ) - expect(options.method).toBe('POST') - - expect(Utils.getRequestHeaders).toBeCalled() - expect(Utils.getRequestHeaders.mock.calls[0][0]['Content-Length']).toBe(12345) - expect(options.headers).toBe(headerMock) - expect(options.auth).toEqual({user: 'space-whales', pass: 'KEY'}) - }) -}) - -test('passes back the parsed json body', () => { - const result = { - test: 1, - api: 'bundles', - progressive: '-web-sdk' - } - - Request.mockClear() - Request.mockImplementation((options, callback) => { - callback(null, {}, JSON.stringify(result)) - }) - - return buildRequest( - { - origin: 'https://test.mobify.com', - projectSlug: 'progressive-web-sdk-tests' - }, - {} - ).then((output) => { - expect(output).toEqual(result) - }) -}) - -test('calls Fail if there is a non-null error', () => { - Request.mockClear() - Request.mockImplementation((options, callback) => { - callback({message: 'Error!'}, {}, '') - }) - Utils.fail.mockClear() - - buildRequest( - { - origin: 'https://test.mobify.com', - projectSlug: 'progressive-web-sdk-tests' - }, - {} - ) - - expect(Utils.fail).toBeCalledWith('Error!') -}) - -test('calls Fail if the response has an error code', () => { - const response = {code: 500} - Request.mockImplementation((options, callback) => { - callback(null, response, '') - }) - Utils.fail.mockClear() - Utils.errorForStatus.mockClear() - Utils.errorForStatus.mockReturnValueOnce({message: 'Internal Server Error'}) - - buildRequest( - { - origin: 'https://test.mobify.com', - projectSlug: 'progressive-web-sdk-tests' - }, - {} - ) - - expect(Utils.fail).toBeCalledWith('Internal Server Error') - expect(Utils.errorForStatus).toBeCalledWith(response) -}) diff --git a/packages/pwa-kit-dev/scripts/file-utils.js b/packages/pwa-kit-dev/scripts/file-utils.js deleted file mode 100644 index 16c2d23780..0000000000 --- a/packages/pwa-kit-dev/scripts/file-utils.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2022, Salesforce, Inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -const fs = require('fs') - -// TODO: Update tests so that fs.promises can be used directly -const promisify = require('util').promisify -const readFileAsync = promisify(fs.readFile) -const writeFileAsync = promisify(fs.writeFile) -const statAsync = promisify(fs.stat) -const mkdirAsync = promisify(fs.mkdir) -const readdirAsync = promisify(fs.readdir) - -const readFile = (path) => readFileAsync(path, 'utf8') -const writeFile = (path, contents) => writeFileAsync(path, contents, 'utf8') - -const writeToPath = (path) => (contents) => writeFile(path, contents) - -const mkdirIfNonexistent = (dirname) => statAsync(dirname).catch(() => mkdirAsync(dirname)) - -const existsSync = (path) => { - try { - fs.statSync(path) - return true - } catch (e) { - return false - } -} - -const filterOnStat = (pathBuilder, statCondition) => async (items) => { - const promises = items.map(async (item) => { - try { - const stats = await statAsync(pathBuilder(item)) - return statCondition(stats) ? item : null - } catch (_) { - // Ignoring the error because we only care about valid paths - return null - } - }) - const results = await Promise.all(promises) - return results.filter((item) => item != null) -} - -const filterDirectories = (pathBuilder) => filterOnStat(pathBuilder, (stats) => stats.isDirectory()) - -const filterFiles = (pathBuilder) => filterOnStat(pathBuilder, (stats) => stats.isFile()) - -const jsonRead = (path) => { - return readFile(path).then((text) => JSON.parse(text)) -} - -const jsonWrite = (path) => (contents) => { - return writeFile(path, JSON.stringify(contents, null, 2)) -} - -module.exports = { - // Just passing through - createWriteStream: fs.createWriteStream, - - // Async fs functions - readdirAsync, - statAsync, - readFileAsync, - - // Local utilities - readFile, - writeFile, - writeToPath, - mkdirIfNonexistent, - existsSync, - filterDirectories, - filterFiles, - jsonRead, - jsonWrite -} diff --git a/packages/pwa-kit-dev/scripts/file-utils.test.js b/packages/pwa-kit-dev/scripts/file-utils.test.js deleted file mode 100644 index 830aafbdc2..0000000000 --- a/packages/pwa-kit-dev/scripts/file-utils.test.js +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -jest.mock('fs') -const fs = require('fs') - -const fileUtils = require('./file-utils') - -const successCallbackAdapter = (value = true) => (...args) => { - args[args.length - 1](null, value) -} - -const failureCallbackAdapter = (value = {}) => (...args) => { - args[args.length - 1](value) -} - -const argsIgnoringCallback = (mock, index = 0) => mock.mock.calls[index].slice(0, -1) - -beforeEach(() => { - jest.resetAllMocks() -}) - -test('readFile reads as utf8', () => { - fs.readFile.mockImplementation(successCallbackAdapter()) - - return fileUtils.readFile('test.dat').then(() => { - expect(fs.readFile).toBeCalled() - expect(fs.readFile.mock.calls[0].slice(0, -1)).toEqual(['test.dat', 'utf8']) - }) -}) - -test('writeFile writes as utf8', () => { - fs.writeFile.mockImplementation(successCallbackAdapter()) - - return fileUtils.writeFile('myfile.json', '{"test": true, "json": "JSON"}').then(() => { - expect(fs.writeFile.mock.calls.length).toBe(1) - expect(fs.writeFile.mock.calls[0].slice(0, -1)).toEqual([ - 'myfile.json', - '{"test": true, "json": "JSON"}', - 'utf8' - ]) - }) -}) - -test('writeToPath returns a function that writes to a given path', () => { - fs.writeFile.mockImplementation(successCallbackAdapter()) - - const writer = fileUtils.writeToPath('test/summary.dat') - - expect(typeof writer).toBe('function') - expect(fs.writeFile).not.toBeCalled() - - return writer('ABC 123').then(() => { - expect(fs.writeFile).toBeCalled() - expect(fs.writeFile.mock.calls[0].slice(0, -1)).toEqual([ - 'test/summary.dat', - 'ABC 123', - 'utf8' - ]) - }) -}) - -test('mkdirIfNonexistent does not make a directory if it exists', () => { - fs.stat.mockImplementation(successCallbackAdapter({test: true})) - - return fileUtils.mkdirIfNonexistent('testdir').then((result) => { - expect(fs.stat).toHaveBeenCalledTimes(1) - expect(argsIgnoringCallback(fs.stat)).toEqual(['testdir']) - expect(fs.mkdir).not.toBeCalled() - expect(result.test).toBe(true) - }) -}) - -test("mkdirIfNonexistent makes a directory if it doesn't exist", () => { - fs.stat.mockImplementation(failureCallbackAdapter('test')) - fs.mkdir.mockImplementation(successCallbackAdapter()) - - return fileUtils.mkdirIfNonexistent('testdir').then(() => { - expect(fs.stat).toBeCalled() - expect(argsIgnoringCallback(fs.stat)).toEqual(['testdir']) - expect(fs.mkdir).toBeCalled() - expect(argsIgnoringCallback(fs.mkdir)).toEqual(['testdir']) - }) -}) - -test('existsSync calls statSync and returns true if it succeeds', () => { - fs.statSync.mockReturnValueOnce(true) - expect(fileUtils.existsSync('test.dat')).toBe(true) - expect(fs.statSync.mock.calls.length).toBe(1) - expect(fs.statSync.mock.calls[0][0]).toBe('test.dat') -}) - -test('existsSync returns false if statSync throws', () => { - fs.statSync.mockImplementation(() => { - throw new Error('test') - }) - - expect(fileUtils.existsSync('test.dat')).toBe(false) -}) - -test('filterDirectories returns the names that are directories after passing through the pathBuilder', () => { - const dirList = ['a', 'c', 'r'] - const itemList = ['a', 'b', 'c', 'r', 's', 't'] - - fs.stat.mockImplementation((fn, callback) => { - if (dirList.indexOf(fn) !== -1) { - callback(null, {isDirectory: () => true}) - } else if (fn === 's') { - callback({err: true}) - } else { - callback(null, {isDirectory: () => false}) - } - }) - - const pathBuilder = jest.fn().mockImplementation((x) => x) - - return fileUtils - .filterDirectories(pathBuilder)(itemList) - .then((result) => { - expect(fs.stat.mock.calls.length).toBe(itemList.length) - expect(pathBuilder.mock.calls.length).toBe(itemList.length) - itemList.forEach((item) => { - expect(pathBuilder).toBeCalledWith(item) - }) - - expect(result).toEqual(dirList) - }) -}) - -test('filterFiles returns the names that are directories after passing through the pathBuilder', () => { - const fileList = ['b', 't'] - const itemList = ['a', 'b', 'c', 'r', 's', 't'] - - fs.stat.mockImplementation((fn, callback) => { - if (fileList.indexOf(fn) !== -1) { - callback(null, {isFile: () => true}) - } else if (fn === 's') { - callback({err: true}) - } else { - callback(null, {isFile: () => false}) - } - }) - - const pathBuilder = jest.fn().mockImplementation((x) => x) - - return fileUtils - .filterFiles(pathBuilder)(itemList) - .then((result) => { - expect(fs.stat.mock.calls.length).toBe(itemList.length) - expect(pathBuilder.mock.calls.length).toBe(itemList.length) - itemList.forEach((item) => { - expect(pathBuilder).toBeCalledWith(item) - }) - - expect(result).toEqual(fileList) - }) -}) - -test('jsonRead reads JSON from a file', () => { - fs.readFile.mockImplementation(successCallbackAdapter('{"test": true}')) - - return fileUtils.jsonRead('test.json').then((result) => { - expect(fs.readFile).toBeCalled() - expect(fs.readFile.mock.calls[0][0]).toBe('test.json') - - expect(result).toEqual({test: true}) - }) -}) - -test('jsonWrite writes JSON to a file', () => { - fs.writeFile.mockImplementation(successCallbackAdapter()) - - return fileUtils - .jsonWrite('test.json')({test: true}) - .then(() => { - expect(fs.writeFile).toBeCalled() - expect(fs.writeFile.mock.calls[0][0]).toBe('test.json') - expect(fs.writeFile.mock.calls[0][1]).toBe('{\n "test": true\n}') - }) -}) diff --git a/packages/pwa-kit-dev/scripts/upload.js b/packages/pwa-kit-dev/scripts/upload.js deleted file mode 100644 index e0cc967ce5..0000000000 --- a/packages/pwa-kit-dev/scripts/upload.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -const Utils = require('./utils') -const buildRequest = require('./build-request') - -const ARCHIVE = 'build.tar' - -const isEmptyOptions = (options) => { - return ( - typeof options === 'undefined' || - typeof options.projectSlug === 'undefined' || - options.projectSlug.length === 0 || - typeof options.origin === 'undefined' || - options.origin.length === 0 - ) -} - -const upload = (options) => { - const dataBufferPromise = Utils.buildObject(ARCHIVE, options).then((buildObj) => { - const buildJSON = JSON.stringify(buildObj, null, 4) - return Buffer.from(buildJSON) - }) - - const credentialsPromise = Utils.readCredentials(options.credentialsFile) - - return Promise.all([dataBufferPromise, credentialsPromise]) - .then((values) => { - const dataBuffer = values[0] - const credentials = values[1] - - const requestOptions = { - username: credentials.username, - api_key: credentials.api_key, - body: dataBuffer, - dataLength: dataBuffer.length, - origin: options.origin, - projectSlug: options.projectSlug, - target: options.target - } - - console.log(`Beginning upload to ${options.origin}`) - return buildRequest(requestOptions, dataBuffer) - }) - .then(() => { - console.log('Bundle Uploaded!') - }) -} - -const uploadBundle = (opts) => { - if (isEmptyOptions(opts)) { - Utils.fail( - '[Error: You must provide a Managed Runtime project slug and Cloud origin to upload a bundle.]' - ) - } - - const defaults = { - buildDirectory: 'build', - credentialsFile: Utils.getCredentialsFile(), - target: '', - message: Utils.setDefaultMessage() - } - - const options = Object.assign({}, defaults, opts) - - // Create bundle will generate the archive file and return an updated - // options object - return Utils.createBundle(options, ARCHIVE).then((updatedOptions) => { - return Utils.exists(ARCHIVE).then(() => upload(updatedOptions)) - }) -} - -module.exports = uploadBundle diff --git a/packages/pwa-kit-dev/scripts/upload.test.js b/packages/pwa-kit-dev/scripts/upload.test.js deleted file mode 100644 index f811965851..0000000000 --- a/packages/pwa-kit-dev/scripts/upload.test.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -jest.mock('./build-request') -jest.mock('./utils') -const Utils = require('./utils') - -const uploadBundle = require('./upload.js') - -let realFail -beforeEach(() => { - realFail = Utils.fail - Utils.fail.mockImplementation((errorMessage) => { - throw new Error(errorMessage) - }) -}) - -afterEach(() => { - Utils.fail = realFail -}) - -const requiredOptions = {projectSlug: 'mobify-test', origin: 'https://cloud-test.mobify.com'} - -test('uploadBundle fails with invalid options', () => { - ;[ - undefined, - {}, - {projectSlug: '', origin: requiredOptions.origin}, - {projectSlug: requiredOptions.projectSlug, origin: ''} - ].forEach((options) => { - try { - uploadBundle(options) - } catch (e) { - expect(Utils.fail).toBeCalled() - expect(e.message).toBe( - '[Error: You must provide a Managed Runtime project slug and Cloud origin to upload a bundle.]' - ) - } - }) -}) - -test("calls Utils.exists to check for the bundle's existence", () => { - Utils.createBundle.mockClear() - Utils.createBundle.mockReturnValueOnce(Promise.resolve()) - - Utils.exists.mockClear() - Utils.exists.mockReturnValueOnce(Promise.reject()) - - Utils.buildObject.mockClear() - - return uploadBundle(requiredOptions) - .catch(() => true) - .then(() => { - expect(Utils.createBundle).toBeCalled() - expect(Utils.exists).toBeCalled() - expect(Utils.exists.mock.calls[0][0]).toBe('build.tar') - expect(Utils.buildObject).not.toBeCalled() - }) -}) - -test('the default options can be overwritten', async () => { - Utils.createBundle.mockClear() - Utils.createBundle.mockReturnValue(Promise.reject()) - - try { - await uploadBundle({target: 'dev', ...requiredOptions}) - } catch (err) { - const outputTarget = Utils.createBundle.mock.calls[0][0].target - expect(outputTarget).toBe('dev') - } - - try { - await uploadBundle(requiredOptions) - } catch (err) { - const outputTarget = Utils.createBundle.mock.calls[1][0].target - const defaultTargetValue = '' // see OPTION_DEFAULTS in ./upload.js - expect(outputTarget).toBe(defaultTargetValue) - } - - Utils.createBundle.mockReset() -}) diff --git a/packages/pwa-kit-dev/scripts/utils.js b/packages/pwa-kit-dev/scripts/utils.js deleted file mode 100644 index c7ee883a29..0000000000 --- a/packages/pwa-kit-dev/scripts/utils.js +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -/* eslint-disable strict */ -'use strict' - -const path = require('path') -const fse = require('fs-extra') -const git = require('git-rev-sync') -const validator = require('validator') - -const archiver = require('archiver') - -const fileUtils = require('./file-utils') - -const Matcher = require('../dist/utils/glob').Matcher - -const request = require('request') - -const SDK_VERSION = require('../package.json').version -const DEFAULT_DOCS_URL = - 'https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pushing-and-deploying-bundles.html' - -const Utils = {} - -// Returns a bundle object ready to upload to the Release Console. -Utils.buildObject = (archivePath, options) => { - options = options || {} - return fileUtils - .readFileAsync(archivePath) - .catch((err) => Utils.fail(err)) - .then((data) => { - // Encode data and assemble object to upload - const base64data = data.toString('base64') - - const bundleMetadata = { - message: options.message || '', - encoding: 'base64', - data: base64data - } - - // Copy any defined SSR parameters from the - // options. - if (options.set_ssr_values) { - for (const key of Object.keys(options).filter((key) => key.startsWith('ssr_'))) { - const value = options[key] - if (value !== undefined) { - bundleMetadata[key] = value - } - } - } - - return bundleMetadata - }) -} - -/** - * @param options - * { - * buildDirectory, - * projectSlug, - * set_ssr_values, - * ssr_only, - * ssr_shared - * } - * @param destination - * @returns {*} - */ -Utils.createBundle = (options, destination) => { - return Utils.exists(options.buildDirectory) - .catch(() => - Utils.fail( - /* eslint-disable max-len */ - `[Error: Build directory at path "${path.join( - process.cwd(), - options.buildDirectory - )}" not found.]\n` + - 'You must first run the Progressive Web SDK build process before uploading a bundle.' - /* eslint-disable max-len */ - ) - ) - .then(() => { - // Clone the options so that we can return a modified object - const returnedOptions = Object.assign({}, options) - - // Build a list of files in the archive - const filesInArchive = [] - - const output = fileUtils.createWriteStream(destination) - const archive = archiver('tar') - - archive.on('error', Utils.fail) - - archive.pipe(output) - - // The new root directory of files, that will replace - // the buildPrefix. Must be of form: /bld/ - const newRoot = path.join(options.projectSlug, 'bld', '') - - // archive.bulk is deprecated, so we use archive.directory to - // walk the tree and add files (under the newRoot path), adding - // files to filesInArchive as we go. - archive.directory( - // We archive the build directory and everything underneath it. - options.buildDirectory, - // We pass an empty destPath because we fix the entry prefix - // in the function. - '', - // This function is called for every file in the tree. - (entry) => { - // entry is a TarEntryData object. - // https://archiverjs.com/docs/global.html#TarEntryData - // The entry.name field is the file path relative to the - // buildDirectory. - if (entry.stats.isFile()) { - filesInArchive.push(entry.name) - } - - // Add a prefix so that the entry in the tar file - // is relative and starts with newRoot - entry.prefix = newRoot - - return entry - } - ) - - return new Promise((resolve) => { - output.on('finish', () => { - // If we're doing an SSR build, we now need - // to update the ssr_only and ssr_shared lists, - // which are supplied to use as minimatch-style - // glob patterns, but must be uploaded as lists - // of actual file paths relative to the build - // directory. - if (options.set_ssr_values) { - const ssrOnlyMatcher = new Matcher(options.ssr_only) - returnedOptions.ssr_only = filesInArchive.filter(ssrOnlyMatcher.filter) - const ssrSharedMatcher = new Matcher(options.ssr_shared) - returnedOptions.ssr_shared = filesInArchive.filter(ssrSharedMatcher.filter) - } - - resolve(returnedOptions) - }) - - // Finalize now that we have set up all the event handlers - archive.finalize() - }) - }) -} - -Utils.errorForStatus = (response) => { - const status = response.statusCode - - if (status < 400) { - return false - } - - let error - try { - error = JSON.parse(response.body) - } catch (err) { - // We set this to an empty object to resolve issues where response.body - // is not a JSON or properly-formatted JSON object - // e.g. response.body === 'Unauthorized' - error = {} - } - - return new Error( - [ - `HTTP ${status}`, - error.message || response.body, - `For more information visit ${error.docs_url || DEFAULT_DOCS_URL}$` - ].join('\n') - ) -} - -Utils.exists = fileUtils.statAsync - -/* istanbul ignore next */ -Utils.fail = (errorMessage) => { - console.error(errorMessage) - process.exit(1) -} - -Utils.getRequestHeaders = (additionalHeaders) => - Object.assign({'User-Agent': `progressive-web-sdk#${SDK_VERSION}`}, additionalHeaders) - -/* istanbul ignore next */ -Utils.getCredentialsFile = () => - `${process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME}/.mobify` - -Utils.readCredentials = (filepath) => { - return Utils.exists(filepath) - .catch( - /* istanbul ignore next */ () => - Utils.fail( - `Credentials file "${filepath}" not found.\n` + - 'Visit https://runtime.commercecloud.com/account/settings for ' + - 'steps on authorizing your computer to push bundles.' - ) - ) - .then(() => fileUtils.readFileAsync(filepath)) - .then((creds) => { - creds = JSON.parse(creds) - - return { - username: creds.username, - api_key: creds.api_key - } - }) - .catch( - /* istanbul ignore next */ (e) => - Utils.fail(`Error parsing "${filepath}".\n` + `[${e}]`) - ) -} - -Utils.readPackageJson = (keyName) => { - try { - // Using the full path isn't strictly necessary, but results in clearer errors - const packageJson = path.join(process.cwd(), 'package.json') - const key = fse.readJsonSync(packageJson)[keyName] - if (!key) Utils.fail(`Error reading ${packageJson}: key '${keyName}' is missing`) - return key - } catch (e) { - Utils.fail(e) - } -} - -Utils.createToken = (project, environment, cloudOrigin, apiKey) => { - const options = { - url: new URL(`/api/projects/${project}/target/${environment}/jwt/`, cloudOrigin).toString(), - method: 'POST', - headers: Utils.getRequestHeaders({ - Accept: 'application/json', - Authorization: `Bearer ${apiKey}` - }) - } - return new Promise((resolve) => { - request(options, (error, response, body) => { - if (error || (error = Utils.errorForStatus(response))) { - Utils.fail(`${cloudOrigin} returned ${error.message}`) - } - resolve(body) - }) - }) - .then((body) => JSON.parse(body).token) - .catch((e) => Utils.fail(e)) -} - -Utils.setDefaultMessage = () => { - try { - return `${git.branch()}: ${git.short()}` - } catch (err) { - if (err.code === 'ENOENT') { - console.log('Please run "git init" to initialize a new Git repository.') - } - return 'PWA Kit Bundle' - } -} - -Utils.delayedPromise = (value, delay) => { - return new Promise((resolve) => { - setTimeout(() => resolve(value), delay) - }) -} - -Utils.handleRequestError = (error) => { - throw new Error(error.message) -} - -Utils.requestErrorMessage = { - code401: 'Invalid api_key.', - code403: - 'You do not have permission to perform this actions.\nPlease double check your command to make sure the option values are correct.', // wrong project name. - code404: - 'Resource not found.\nPlease double check your command to make sure the option values are correct.', // wrong target name - code500: 'Internal Server Error. Please report this to the Salesforce support team.' -} - -/** - * @param {string} log - * @returns {Object} - * { - * level, - * message, - * requestId - * } - */ -Utils.parseLog = (log) => { - const parts = log.trim().split('\t') - let requestId, shortRequestId, message, level - - if ( - parts.length >= 3 && - validator.isISO8601(parts[0]) && - validator.isUUID(parts[1]) && - validator.isAlpha(parts[2]) - ) { - // An application log - parts.shift() - requestId = parts.shift() - level = parts.shift() - } else { - // A platform log - const words = parts[0].split(' ') - level = words.shift() - parts[0] = words.join(' ') - } - message = parts.join('\t') - - const match = /(?[a-f\d]{8})/.exec(requestId || message) - if (match) { - shortRequestId = match.groups.id - } - - return {level, message, shortRequestId} -} - -module.exports = Utils diff --git a/packages/pwa-kit-dev/scripts/utils.test.js b/packages/pwa-kit-dev/scripts/utils.test.js deleted file mode 100644 index 2d6e1e6d90..0000000000 --- a/packages/pwa-kit-dev/scripts/utils.test.js +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -const Utils = require('./utils') - -const fs = require('fs') -const fse = require('fs-extra') -const path = require('path') -const os = require('os') -const rimraf = require('rimraf') -const pkg = require('../package.json') - -jest.mock('git-rev-sync') -const git = require('git-rev-sync') - -jest.mock('request') -const request = require('request') - -let realFail -beforeEach(() => { - realFail = Utils.fail - Utils.fail = jest.fn() -}) - -afterEach(() => { - Utils.fail = realFail -}) - -test('getRequestHeaders sets the User-Agent header', () => { - const result = Utils.getRequestHeaders() - expect(result['User-Agent']).toBe(`progressive-web-sdk#${pkg.version}`) -}) - -test('getRequestHeaders copies over headers from the passed object', () => { - const additionalHeaders = { - Cryptography: 'none', - Context: 'testing', - Connections: 'mocked out' - } - - const result = Utils.getRequestHeaders(additionalHeaders) - - Object.keys(additionalHeaders).forEach((key) => { - expect(result[key]).toBe(additionalHeaders[key]) - }) -}) - -test('errorForStatus returns false for 2xx and 3xx statuses', () => { - ;[200, 201, 302, 303, 304].forEach((statusCode) => { - expect(Utils.errorForStatus({statusCode})).toBe(false) - }) -}) - -test('errorForStatus returns an Error for 4xx and 5xx statuses', () => { - ;[400, 401, 403, 404, 500, 503].forEach((statusCode) => { - expect(Utils.errorForStatus({statusCode})).toBeInstanceOf(Error) - }) -}) - -describe('readPackageJson', () => { - const data = {name: 'test'} - let mockReadJsonSync - beforeAll(() => { - mockReadJsonSync = jest.spyOn(fse, 'readJsonSync').mockImplementation(() => data) - }) - - afterAll(() => { - mockReadJsonSync.mockRestore() - }) - - test('returns key value', () => { - const keyName = 'name' - const packageJson = path.join(process.cwd(), 'package.json') - expect(Utils.readPackageJson(keyName)).toBe(data[keyName]) - expect(fse.readJsonSync).toBeCalledWith(packageJson) - }) - - test('fails if key is missing', () => { - const keyName = 'fake' - expect(Utils.readPackageJson(keyName)).toBeUndefined() - expect(fse.readJsonSync).toBeCalled() - expect(Utils.fail).toBeCalled() - - const errorMessage = Utils.fail.mock.calls[0][0] - expect(errorMessage.includes(`key '${keyName}' is missing`)).toBeTruthy() - }) -}) - -describe('createToken', () => { - let args = { - project: 'pwa-kit', - environment: 'dev', - cloudOrigin: 'https://cloud-test.mobify.com', - apiKey: 'key' - } - - test('makes request and returns token', async () => { - const data = {token: 'abcd'} - request.mockClear() - request.mockImplementationOnce((_, callback) => { - callback(null, {}, JSON.stringify(data)) - }) - - const mockGetHeaders = jest.fn((headers) => headers) - Utils.getRequestHeaders = mockGetHeaders - - expect(await Utils.createToken(...Object.values(args))).toBe(data.token) - expect(request).toBeCalled() - expect(mockGetHeaders).toBeCalled() - - const options = request.mock.calls[0][0] - expect(options.url).toBe( - `${args.cloudOrigin}/api/projects/${args.project}/target/${args.environment}/jwt/` - ) - expect(options.method).toBe('POST') - expect(options.headers).toStrictEqual({ - Accept: 'application/json', - Authorization: `Bearer ${args.apiKey}` - }) - }) - - test('fails after unsuccessful request', async () => { - const error = {statusCode: 403} - request.mockClear() - request.mockImplementationOnce((_, callback) => { - callback(null, error, {}) - }) - - expect(await Utils.createToken(...Object.values(args))).toBeUndefined() - expect(request).toBeCalled() - expect(Utils.fail).toBeCalled() - - const errorMessage = Utils.fail.mock.calls[0][0] - expect( - errorMessage.includes(`${args.cloudOrigin} returned HTTP ${error.statusCode}`) - ).toBeTruthy() - }) -}) - -describe('setDefaultMessage', () => { - test('Bundle message is set to branch and commit hash', () => { - git.branch.mockClear() - git.short.mockClear() - git.branch.mockReturnValueOnce('develop') - git.short.mockReturnValueOnce('4cd54ec') - - expect(Utils.setDefaultMessage()).toBe('develop: 4cd54ec') - }) - - test('Bundle message defaults properly if git branch fails', () => { - git.branch.mockImplementationOnce(() => { - throw new Error('Failwhale') - }) - - expect(Utils.setDefaultMessage()).toBe('PWA Kit Bundle') - }) - - test('Bundle message defaults properly if git short fails', () => { - git.short.mockImplementationOnce(() => { - throw new Error('Failwhale') - }) - - expect(Utils.setDefaultMessage()).toBe('PWA Kit Bundle') - }) - - test('Test message is printed if we have an ENOENT', () => { - git.branch.mockImplementationOnce(() => { - throw {code: 'ENOENT'} - }) - const consoleLog = console.log - const mockConsoleLog = jest.fn() - console.log = mockConsoleLog - - try { - Utils.setDefaultMessage() - } finally { - console.log = consoleLog - } - - expect(mockConsoleLog).toBeCalled() - expect(mockConsoleLog.mock.calls[0][0].includes('git init')).toBe(true) - }) -}) - -describe('Create Bundle', () => { - const cases = [ - { - name: 'Should create a tar file from a bundle directory', - files: [ - path.posix.join('directory-a', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ], - expectedSSROnly: [ - path.posix.join('directory-a', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ], - expectedSSRShared: [ - path.posix.join('directory-a', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ] - }, - { - name: 'Should properly handle directory names that look like file names', - files: [ - path.posix.join('npm-library.js', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ], - expectedSSROnly: [ - path.posix.join('npm-library.js', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ], - expectedSSRShared: [ - path.posix.join('npm-library.js', '1.js'), - path.posix.join('directory-b', '2.js'), - path.posix.join('directory-c', '3.js') - ] - } - ] - - cases.forEach(({name, files, expectedSSROnly, expectedSSRShared}) => { - test(name, () => { - const tmp = fs.mkdtempSync(path.resolve(os.tmpdir(), 'create-bundle-test-')) - const bundlePath = path.join(tmp, 'bundle') - fs.mkdirSync(bundlePath) - - const tarFilePath = path.join(tmp, 'bundle.tar') - - /* Generate all directories in the file path recursively if they don't exist */ - const generateDirectories = (filePath) => { - const dirname = path.dirname(filePath) - if (fs.existsSync(dirname)) { - return true - } - generateDirectories(dirname) - fs.mkdirSync(dirname) - } - - /* Generate directories and files */ - files.forEach((f) => { - generateDirectories(path.join(bundlePath, f)) - fs.closeSync(fs.openSync(path.join(bundlePath, f), 'w')) - }) - - return Promise.resolve() - .then(() => - Utils.createBundle( - { - buildDirectory: bundlePath, - projectSlug: 'retail-react-app', - set_ssr_values: true, - ssr_only: ['**/*.js'], - ssr_shared: ['**/*.js'] - }, - tarFilePath - ) - ) - .then((result) => { - expect(fs.existsSync(tarFilePath)) - expect(result.ssr_only.sort()).toEqual(expectedSSROnly.sort()) - expect(result.ssr_shared.sort()).toEqual(expectedSSRShared.sort()) - }) - .finally(() => rimraf.sync(tmp)) - }) - }) -}) - -test('parseLog parses application and platform logs correctly', () => { - const requestId = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - const shortRequestId = requestId.slice(0, 8) - const cases = [ - { - log: `START RequestId: ${requestId} Version: $LATEST`, - expected: { - level: 'START', - message: `RequestId: ${requestId} Version: $LATEST`, - shortRequestId - } - }, - { - log: `END RequestId: ${requestId}`, - expected: { - level: 'END', - message: `RequestId: ${requestId}`, - shortRequestId - } - }, - { - log: `REPORT RequestId: ${requestId}\tDuration: 21.04 ms\tBilled Duration: 22 ms\tMemory Size: 2496 MB\tMax Memory Used: 94 MB`, - expected: { - level: 'REPORT', - message: `RequestId: ${requestId}\tDuration: 21.04 ms\tBilled Duration: 22 ms\tMemory Size: 2496 MB\tMax Memory Used: 94 MB`, - shortRequestId - } - }, - { - log: `2022-10-31T22:00:00.000Z\t${requestId}\tINFO\tRequest: GET /`, - expected: { - level: 'INFO', - message: 'Request: GET /', - shortRequestId - } - }, - { - log: `2022-10-31T22:00:00.000Z\t${requestId}\tERROR\tResponse status: 500\tuh oh!`, - expected: { - level: 'ERROR', - message: 'Response status: 500\tuh oh!', - shortRequestId - } - }, - { - log: `2022-10-31T22:00:00.000Z\t${requestId}\tINFO\t`, - expected: { - level: 'INFO', - message: '', - shortRequestId - } - } - ] - cases.forEach(({log, expected}) => { - const parsed = Utils.parseLog(log) - Object.keys(parsed).forEach((key) => { - expect(parsed[key]).toBe(expected[key]) - }) - }) -}) diff --git a/packages/pwa-kit-dev/scripts/version.js b/packages/pwa-kit-dev/scripts/version.js index 6a9c724dd3..792011c28e 100644 --- a/packages/pwa-kit-dev/scripts/version.js +++ b/packages/pwa-kit-dev/scripts/version.js @@ -11,10 +11,7 @@ const path = require('path') const os = require('os') const fs = require('fs') -const date = new Date() - .toString() - .split(' ') - .slice(1, 4) +const date = new Date().toString().split(' ').slice(1, 4) const heading = `## v${pkg.version} (${date[0]} ${date[1]}, ${date[2]})\n` const changelog = path.resolve(os.tmpdir(), 'CHANGELOG.md') diff --git a/packages/pwa-kit-dev/src/configs/eslint/eslint-config.js b/packages/pwa-kit-dev/src/configs/eslint/eslint-config.js index e6813adc5e..818c691ead 100644 --- a/packages/pwa-kit-dev/src/configs/eslint/eslint-config.js +++ b/packages/pwa-kit-dev/src/configs/eslint/eslint-config.js @@ -23,7 +23,7 @@ module.exports = { plugins: ['header', 'react', 'prettier'], settings: { react: { - version: '16.8' + version: 'detect' } }, rules: { diff --git a/packages/pwa-kit-dev/src/configs/jest/jest.config.js b/packages/pwa-kit-dev/src/configs/jest/jest.config.js index 2069e43fd1..0ba0b3aa07 100644 --- a/packages/pwa-kit-dev/src/configs/jest/jest.config.js +++ b/packages/pwa-kit-dev/src/configs/jest/jest.config.js @@ -13,11 +13,8 @@ module.exports = { testEnvironment: 'jsdom', testPathIgnorePatterns: ['node_modules', 'build'], moduleNameMapper: { - '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': path.join( - __dirname, - 'mocks', - 'fileMock.js' - ), + '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': + path.join(__dirname, 'mocks', 'fileMock.js'), '\\.(svg)$': path.join(__dirname, 'mocks', 'svgMock.js'), '\\.(css|less)$': path.join(__dirname, 'mocks', 'styleMock.js') }, diff --git a/packages/pwa-kit-dev/src/ssr/server/build-dev-server.test.js b/packages/pwa-kit-dev/src/ssr/server/build-dev-server.test.js index 7ba763518e..96b6ee0f10 100644 --- a/packages/pwa-kit-dev/src/ssr/server/build-dev-server.test.js +++ b/packages/pwa-kit-dev/src/ssr/server/build-dev-server.test.js @@ -360,7 +360,7 @@ describe('DevServer proxying', () => { .get(targetPath) .reply( 200, - function() { + function () { requestHeaders.push(this.req.headers) }, responseHeaders @@ -414,9 +414,7 @@ describe('DevServer proxying', () => { test('restricts methods', () => { // Use nock to mock out a host to which we proxy, though we // do not expect the request to be made. - const nockResponse = nock('https://test.proxy.com') - .get('/test/path3') - .reply(200, 'OK') + const nockResponse = nock('https://test.proxy.com').get('/test/path3').reply(200, 'OK') const app = NoWebpackDevServerFactory._createApp(opts()) const path = '/mobify/caching/base3/test/path3' @@ -434,7 +432,7 @@ describe('DevServer proxying', () => { // Use nock to mock out a host to which we proxy const nockResponse = nock('https://test.proxy.com') .get('/test/path3') - .reply(200, function() { + .reply(200, function () { const headers = this.req.headers expect('x-mobify-access-key' in headers).toBe(false) expect('cache-control' in headers).toBe(false) @@ -474,9 +472,7 @@ describe('DevServer proxying', () => { test('handles error', () => { const app = NoWebpackDevServerFactory._createApp(opts()) - return request(app) - .get('/mobify/proxy/base2/test/path') - .expect(500) + return request(app).get('/mobify/proxy/base2/test/path').expect(500) }) }) @@ -560,7 +556,7 @@ describe('DevServer persistent caching support', () => { route = null }) - test('Caching of compressed responses', () => { + test('No caching of compressed responses', () => { // ADN-118 reported that a cached response was correctly sent // the first time, but was corrupted the second time. This // test is specific to that issue. @@ -582,12 +578,12 @@ describe('DevServer persistent caching support', () => { namespace }) ) - .then((entry) => expect(entry.found).toBe(true)) + .then((entry) => expect(entry.found).toBe(false)) .then(() => request(app).get(url)) .then((res) => app._requestMonitor._waitForResponses().then(() => res)) .then((res) => { expect(res.status).toEqual(200) - expect(res.headers['x-mobify-from-cache']).toEqual('true') + expect(res.headers['x-mobify-from-cache']).toEqual('false') expect(res.headers['content-encoding']).toEqual('gzip') expect(res.text).toEqual(expected) }) @@ -603,20 +599,15 @@ describe('DevServer persistent caching support', () => { expect(res.status).toEqual(200) expect(res.headers['x-mobify-from-cache']).toEqual('false') expect(res.headers['content-encoding']).toEqual('gzip') - return res }) - .then((res) => - app.applicationCache - .get({ - key: keyFromURL(url), - namespace - }) - .then((entry) => ({res, entry})) + .then(() => + app.applicationCache.get({ + key: keyFromURL(url), + namespace + }) ) - .then(({res, entry}) => { - expect(entry.found).toBe(true) - const uncompressed = zlib.gunzipSync(entry.data) - expect(uncompressed.toString()).toEqual(res.text) + .then((entry) => { + expect(entry.found).toBe(false) }) }) }) @@ -748,9 +739,7 @@ describe('DevServer service worker', () => { test(`${name} (and handle 404s correctly)`, () => { const app = createApp() - return request(app) - .get(requestPath) - .expect(404) + return request(app).get(requestPath).expect(404) }) }) }) @@ -795,9 +784,7 @@ describe('DevServer serveStaticFile', () => { const app = NoWebpackDevServerFactory._createApp(options) app.__devMiddleware = MockWebpackDevMiddleware app.use('/test', NoWebpackDevServerFactory.serveStaticFile('static/favicon.ico')) - return request(app) - .get('/test') - .expect(200) + return request(app).get('/test').expect(200) }) test('should return 404 if static file does not exist', async () => { @@ -805,8 +792,6 @@ describe('DevServer serveStaticFile', () => { const app = NoWebpackDevServerFactory._createApp(options) app.__devMiddleware = MockWebpackDevMiddleware app.use('/test', NoWebpackDevServerFactory.serveStaticFile('static/IDoNotExist.ico')) - return request(app) - .get('/test') - .expect(404) + return request(app).get('/test').expect(404) }) }) diff --git a/packages/pwa-kit-dev/src/utils/glob.js b/packages/pwa-kit-dev/src/utils/glob.js deleted file mode 100644 index ee069aeded..0000000000 --- a/packages/pwa-kit-dev/src/utils/glob.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -/* - This file is shared between SDK code and the command-line uploader. - */ - -import minimatch from 'minimatch' - -/** - * The minimatch options we use. Behaviour is intended to match that - * - * @private - * @type {{nocomment: boolean}} - */ -const STANDARD_OPTIONS = { - nocomment: true -} - -/** - * A Matcher class is constructed with a list of minimatch glob patterns. - * Once constructed, it can efficiently test whether a given file path - * matches any of those patterns. Empty patterns are ignored. - * - * @private - */ -export class Matcher { - constructor(patterns) { - // For each input pattern, generate a Minimatch object. Discard - // any that have an empty pattern. - const allPatterns = (patterns || []) - .map((pattern) => new minimatch.Minimatch(pattern, STANDARD_OPTIONS)) - .filter((pattern) => !pattern.empty) - this._positivePatterns = allPatterns.filter((pattern) => !pattern.negate) - this._negativePatterns = allPatterns.filter((pattern) => pattern.negate) - } - - /** - * Return true if the path matches any of the given patterns. - * - * The patterns can include negations, so matching is done against all - * the patterns. A match is true if a given path matches any pattern and - * does not match any negating patterns. - * - * Because the matcher does not support empty patterns, an empty - * path will never match. - * - * @param path {String} the path to be matched - * @returns {boolean} true for a match, false if no match - */ - matches(path) { - if (path) { - const positive = this._positivePatterns.some((pattern) => pattern.match(path)) - - // A negative pattern.match returns true if the - // path does NOT match it. - const negative = this._negativePatterns.some((pattern) => !pattern.match(path)) - - return positive && !negative - } - return false - } - - /** - * Returns a matching function suitable for use with - * Array.filter, Array.some, etc - */ - get filter() { - return (path) => this.matches(path) - } -} diff --git a/packages/pwa-kit-dev/src/utils/glob.test.js b/packages/pwa-kit-dev/src/utils/glob.test.js deleted file mode 100644 index 603701d844..0000000000 --- a/packages/pwa-kit-dev/src/utils/glob.test.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021, salesforce.com, inc. - * All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ -import assert from 'assert' -import {Matcher} from './glob' - -test('Matcher with no patterns matches nothing', () => { - const matcher = new Matcher() - expect(matcher.matches('')).toBe(false) - expect(matcher.matches('a.js')).toBe(false) - expect(matcher.matches()).toBe(false) -}) - -describe('Matcher class filters correctly', () => { - const patterns = ['ssr.js', '**/*.jpg', '!**/no.jpg', 'abc.{js,jsx}'] - - const matcher = new Matcher(patterns) - - // Paths we expect to match - const expectToMatch = [ - 'ssr.js', - 'test1.jpg', - 'static/test2.jpg', - 'static/assets/test3.jpg', - 'abc.js', - 'abc.jsx' - ] - - expectToMatch.forEach((path) => - test(`Expect path "${path}" to match`, () => { - assert.ok(matcher.matches(path), `Expected path "${path}" to be matched`) - }) - ) - - // Paths we expect not to match - const expectNotToMatch = ['ssrxjs', 'subdirectory/ssr.js', 'no.jpg', 'static/no.jpg', 'abc.jsz'] - - expectNotToMatch.forEach((path) => - test(`Expect path "${path}" to NOT match`, () => { - assert.ok(!matcher.matches(path), `Expected path "${path}" to NOT be matched`) - }) - ) - - // Combine the paths into one array and shuffle it - const allPaths = expectToMatch.concat(expectNotToMatch).sort(() => Math.random() - 0.5) - test('Matcher.filter works', () => { - const matched = allPaths.filter(matcher.filter) - assert.strictEqual( - matched.length, - expectToMatch.length, - 'Expected that all matches would be returned by filter' - ) - }) -}) diff --git a/packages/pwa-kit-dev/src/utils/script-utils.test.js b/packages/pwa-kit-dev/src/utils/script-utils.test.js new file mode 100644 index 0000000000..7106b3c836 --- /dev/null +++ b/packages/pwa-kit-dev/src/utils/script-utils.test.js @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2021, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import {mkdtemp, rmdir, writeFile} from 'fs/promises' + +const pkg = require('../../package.json') +import * as scriptUtils from './script-utils' +import path from 'path' +import os from 'os' + +describe('scriptUtils', () => { + const originalEnv = process.env + let tmpDir + + beforeEach(async () => { + process.env = {...originalEnv} + tmpDir = await mkdtemp(path.join(os.tmpdir(), 'scriptUtils-tests')) + }) + + afterEach(async () => { + process.env = originalEnv + tmpDir && (await rmdir(tmpDir, {recursive: true})) + }) + + test('glob() with no patterns matches nothing', () => { + const matcher = scriptUtils.glob() + expect(matcher('')).toBe(false) + expect(matcher('a.js')).toBe(false) + expect(matcher()).toBe(false) + }) + + describe('glob() filters correctly', () => { + const patterns = ['ssr.js', '**/*.jpg', '!**/no.jpg', 'abc.{js,jsx}'] + + const matcher = scriptUtils.glob(patterns) + + // Paths we expect to match + const expectToMatch = [ + 'ssr.js', + 'test1.jpg', + 'static/test2.jpg', + 'static/assets/test3.jpg', + 'abc.js', + 'abc.jsx' + ] + + expectToMatch.forEach((path) => + test(`Expect path "${path}" to match`, () => { + expect(matcher(path)).toBe(true) + }) + ) + + // Paths we expect not to match + const expectNotToMatch = [ + 'ssrxjs', + 'subdirectory/ssr.js', + 'no.jpg', + 'static/no.jpg', + 'abc.jsz' + ] + + expectNotToMatch.forEach((path) => + test(`Expect path "${path}" to NOT match`, () => { + expect(matcher(path)).toBe(false) + }) + ) + + const allPaths = expectToMatch.concat(expectNotToMatch) + + test('glob works with Array.filter', () => { + const matched = allPaths.filter(matcher) + expect(matched.length).toStrictEqual(expectToMatch.length) + }) + }) + + describe('CloudAPIClient', () => { + const username = 'user123' + const api_key = '123' + const encoded = Buffer.from(`${username}:${api_key}`, 'binary').toString('base64') + const expectedAuthHeader = {Authorization: `Basic ${encoded}`} + + test('getAuthHeader', async () => { + const client = new scriptUtils.CloudAPIClient({credentials: {username, api_key}}) + expect(client.getAuthHeader()).toEqual(expectedAuthHeader) + }) + + test('getHeaders', async () => { + const client = new scriptUtils.CloudAPIClient({credentials: {username, api_key}}) + expect(await client.getHeaders()).toEqual({ + 'User-Agent': `${pkg.name}@${pkg.version}`, + ...expectedAuthHeader + }) + }) + }) + + test('getPkgJSON', async () => { + const pkg = await scriptUtils.getPkgJSON() + expect(pkg.name).toBe('pwa-kit-dev') + }) + + describe('defaultMessage', () => { + test('works', async () => { + const mockGit = {branch: () => 'branch', short: () => 'short'} + expect(scriptUtils.defaultMessage(mockGit)).toEqual('branch: short') + }) + + test('works outside of a git repo ', async () => { + const mockGit = { + branch: () => { + throw {code: 'ENOENT'} + }, + short: () => 'short' + } + expect(scriptUtils.defaultMessage(mockGit)).toEqual('PWA Kit Bundle') + }) + + test('works with any other error ', async () => { + const mockGit = { + branch: () => { + throw new Error() + }, + short: () => 'short' + } + expect(scriptUtils.defaultMessage(mockGit)).toEqual('PWA Kit Bundle') + }) + }) + + test('getCredentialsFile', async () => { + expect(scriptUtils.getCredentialsFile('https://example.com', '/path/to/.mobify')).toBe( + '/path/to/.mobify' + ) + expect(scriptUtils.getCredentialsFile('https://example.com', undefined)).toBe( + path.join(os.homedir(), '.mobify--example.com') + ) + expect(scriptUtils.getCredentialsFile('https://cloud.mobify.com', undefined)).toBe( + path.join(os.homedir(), '.mobify') + ) + }) + + describe('readCredentials', () => { + test('should work', async () => { + const creds = {username: 'alice', api_key: 'xyz'} + const thePath = path.join(tmpDir, '.mobify.test') + await writeFile(thePath, JSON.stringify(creds), 'utf8') + expect(await scriptUtils.readCredentials(thePath)).toEqual(creds) + }) + + test('should throw', async () => { + const thePath = path.join(tmpDir, '.mobify.test') + await expect(async () => await scriptUtils.readCredentials(thePath)).rejects.toThrow( + Error + ) + }) + }) + + describe('createBundle', () => { + test('should throw if ssr_only and ssr_shared is empty', async () => { + await expect( + async () => + await scriptUtils.createBundle({ + message: null, + ssr_parameters: {}, + ssr_only: [], + ssr_shared: [], + buildDirectory: tmpDir, + projectSlug: 'slug' + }) + ).rejects.toThrow('no ssrOnly or ssrShared files are defined') + }) + + test('should throw buildDir does not exist', async () => { + await expect( + async () => + await scriptUtils.createBundle({ + message: null, + ssr_parameters: {}, + ssr_only: ['*.js'], + ssr_shared: ['*.js'], + buildDirectory: path.join(tmpDir, 'does-not-exist'), + projectSlug: 'slug' + }) + ).rejects.toThrow('Build directory at path') + }) + + test('should archive a bundle', async () => { + const message = 'message' + const bundle = await scriptUtils.createBundle({ + message, + ssr_parameters: {}, + ssr_only: ['*.js'], + ssr_shared: ['**/*.*'], + buildDirectory: path.join(__dirname, 'test-fixtures', 'minimal-built-app'), + projectSlug: 'slug' + }) + + expect(bundle.message).toEqual(message) + expect(bundle.encoding).toEqual('base64') + expect(bundle.ssr_parameters).toEqual({}) + expect(bundle.ssr_only).toEqual(['ssr.js']) + expect(bundle.ssr_shared).toEqual(['ssr.js', 'static/favicon.ico']) + + // De-code and re-encode gives the same result, to show that it *is* b64 encoded + expect(Buffer.from(bundle.data, 'base64').toString('base64')).toEqual(bundle.data) + }) + }) + + describe('pushBundle', () => { + test.each([ + [ + { + projectSlug: 'project-slug', + targetSlug: undefined, + expectedURL: 'https://cloud.mobify.com/api/projects/project-slug/builds/', + status: 200 + } + ], + [ + { + projectSlug: 'project-slug', + targetSlug: 'target-slug', + expectedURL: + 'https://cloud.mobify.com/api/projects/project-slug/builds/target-slug/', + status: 200 + } + ], + [ + { + projectSlug: 'project-slug', + targetSlug: undefined, + expectedURL: 'https://cloud.mobify.com/api/projects/project-slug/builds/', + status: 401 + } + ] + ])( + 'should push a built bundle and handle status codes (%p)', + async ({projectSlug, targetSlug, expectedURL, status}) => { + const message = 'message' + const bundle = await scriptUtils.createBundle({ + message, + ssr_parameters: {}, + ssr_only: ['*.js'], + ssr_shared: ['**/*.*'], + buildDirectory: path.join(__dirname, 'test-fixtures', 'minimal-built-app'), + projectSlug + }) + + const username = 'user123' + const api_key = '123' + const credentials = {username, api_key} + + const goodResponseBody = {anything: 'anything'} + + // Older APIs on Cloud return JSON for good responses and text for errors, + // hence the strange looking mock response setup. + const text = () => + status === 200 + ? Promise.resolve(JSON.stringify(goodResponseBody)) + : Promise.resolve('An error occurred') + + const json = () => + status === 200 ? Promise.resolve(goodResponseBody) : Promise.reject() + + const responseMock = {status, text, json} + const fetchMock = jest.fn(async () => responseMock) + + const client = new scriptUtils.CloudAPIClient({credentials, fetch: fetchMock}) + + const fn = async () => await client.push(bundle, projectSlug, targetSlug) + + if (status === 200) { + expect(await fn()).toBe(goodResponseBody) + } else { + await expect(fn).rejects.toThrow('For more information visit') + } + + expect(fetchMock).toHaveBeenCalledTimes(1) + + expect(fetchMock).toHaveBeenCalledWith( + expectedURL, + expect.objectContaining({ + body: expect.anything(Buffer), + method: 'POST', + headers: { + Authorization: expect.stringMatching(/^Basic /), + 'Content-Length': expect.stringMatching(/^\d+$/), + 'User-Agent': `${pkg.name}@${pkg.version}` + } + }) + ) + } + ) + }) +}) diff --git a/packages/pwa-kit-dev/src/utils/script-utils.ts b/packages/pwa-kit-dev/src/utils/script-utils.ts new file mode 100644 index 0000000000..3bbd53620d --- /dev/null +++ b/packages/pwa-kit-dev/src/utils/script-utils.ts @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2022, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import os from 'os' +import path from 'path' +import archiver from 'archiver' +import _fetch from 'node-fetch' +import {URL} from 'url' +import {readFile, stat, mkdtemp, rmdir} from 'fs/promises' +import {createWriteStream} from 'fs' +import {readJson} from 'fs-extra' +import {Minimatch} from 'minimatch' +import git from 'git-rev-sync' +import validator from 'validator' + +export const DEFAULT_CLOUD_ORIGIN = 'https://cloud.mobify.com' +export const DEFAULT_DOCS_URL = + 'https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pushing-and-deploying-bundles.html' + +interface Credentials { + username: string + api_key: string +} + +interface CloudAPIClientOpts { + credentials: Credentials + origin?: string + fetch?: _fetch +} + +interface StringMap { + [key: string]: string +} + +interface Bundle { + message: string + encoding: string + data: string + ssr_parameters: object + ssr_only: string[] + ssr_shared: string[] +} + +interface Pkg { + name: string + version: string +} + +/** + * Get the package info for pwa-kit-dev. + */ +export const getPkgJSON = async (): Promise => { + const candidates = [ + path.join(__dirname, '..', 'package.json'), + path.join(__dirname, '..', '..', 'package.json') + ] + for (const candidate of candidates) { + try { + const data = await readJson(candidate) + return data as Pkg + } catch { + // Keep looking + } + } + return {name: 'pwa-kit-dev', version: 'unknown'} +} + +/** + * Get the package info for the current project. + */ +export const getProjectPkg = async (): Promise => { + const p = path.join(process.cwd(), 'package.json') + try { + const data = await readJson(p) + return data as Pkg + } catch { + throw new Error(`Could not read project package at "${p}"`) + } +} + +export class CloudAPIClient { + private opts: Required + + constructor(params: CloudAPIClientOpts) { + this.opts = { + origin: params.origin || DEFAULT_CLOUD_ORIGIN, + fetch: params.fetch || _fetch, + credentials: params.credentials + } + } + + private getAuthHeader(): StringMap { + const {username, api_key} = this.opts.credentials + const encoded = Buffer.from(`${username}:${api_key}`, 'binary').toString('base64') + return {Authorization: `Basic ${encoded}`} + } + + private async getHeaders(): Promise { + const pkg = await getPkgJSON() + return { + 'User-Agent': `${pkg.name}@${pkg.version}`, + ...this.getAuthHeader() + } + } + + private async throwForStatus(res: _fetch.Response) { + if (res.status < 400) { + return + } + + const body = await res.text() + let error + try { + error = JSON.parse(body) + } catch (err) { + error = {} // Cloud doesn't always return JSON + } + + throw new Error( + [ + `HTTP ${res.status}`, + error.message || body, + `For more information visit ${error.docs_url || DEFAULT_DOCS_URL}` + ].join('\n') + ) + } + + async push(bundle: Bundle, projectSlug: string, target?: string) { + const base = `api/projects/${projectSlug}/builds/` + const pathname = target ? base + `${target}/` : base + const url = new URL(this.opts.origin) + url.pathname = pathname + + const body = Buffer.from(JSON.stringify(bundle)) + const headers = { + ...(await this.getHeaders()), + 'Content-Length': body.length.toString() + } + + const res = await this.opts.fetch(url.toString(), { + body, + method: 'POST', + headers + }) + await this.throwForStatus(res) + return await res.json() + } + + async createLoggingToken(project: string, environment: string): Promise { + const url = new URL(this.opts.origin) + url.pathname = `/api/projects/${project}/target/${environment}/jwt/` + const headers = { + ...(await this.getHeaders()), + // Annoyingly, the new logging endpoint only accepts an + // Authorization header that is inconsistent with our older APIs! + Authorization: `Bearer ${this.opts.credentials.api_key}` + } + const res = await this.opts.fetch(url.toString(), { + method: 'POST', + headers + }) + await this.throwForStatus(res) + const data = await res.json() + return data['token'] + } +} + +export const defaultMessage = (gitInstance: git = git): string => { + try { + return `${gitInstance.branch()}: ${gitInstance.short()}` + } catch (err) { + if (err.code === 'ENOENT') { + console.log( + 'Using default bundle message as no message was provided and not in a Git repo.' + ) + } + return 'PWA Kit Bundle' + } +} + +interface CreateBundleArgs { + message: string | null | undefined + ssr_parameters: any + ssr_only: string[] + ssr_shared: string[] + buildDirectory: string + projectSlug: string +} + +export const createBundle = async ({ + message, + ssr_parameters, + ssr_only, + ssr_shared, + buildDirectory, + projectSlug +}: CreateBundleArgs): Promise => { + message = message || defaultMessage() + + const tmpDir = await mkdtemp(path.join(os.tmpdir(), 'pwa-kit-dev-')) + const destination = path.join(tmpDir, 'build.tar') + const filesInArchive = [] + + if (ssr_only.length === 0 || ssr_shared.length === 0) { + throw new Error('no ssrOnly or ssrShared files are defined') + } + + return Promise.resolve() + .then(() => stat(buildDirectory)) + .catch((e) => { + const fullPath = path.join(process.cwd(), buildDirectory) + throw new Error( + `Build directory at path "${fullPath}" not found.\n` + + 'Run `pwa-kit-dev build` first!' + ) + }) + .then( + () => + new Promise((resolve, reject) => { + const output = createWriteStream(destination) + const archive = archiver('tar') + + archive.pipe(output) + + // See https://archiverjs.com/docs/global.html#TarEntryData + const newRoot = path.join(projectSlug, 'bld', '') + archive.directory(buildDirectory, '', (entry) => { + if (entry.stats.isFile()) { + filesInArchive.push(entry.name) + } + entry.prefix = newRoot + return entry + }) + + archive.on('error', reject) + output.on('finish', resolve) + + archive.finalize() + }) + ) + .then(() => readFile(destination)) + .then((data) => { + const encoding = 'base64' + return { + message, + encoding, + data: data.toString(encoding), + ssr_parameters, + ssr_only: filesInArchive.filter(glob(ssr_only)), + ssr_shared: filesInArchive.filter(glob(ssr_shared)) + } + }) + .finally(() => rmdir(tmpDir, {recursive: true})) +} + +type MatchFn = (a: string) => boolean + +export const glob = (patterns?: string[]): MatchFn => { + // The patterns can include negations, so matching is done against all + // the patterns. A match is true if a given path matches any pattern and + // does not match any negating patterns. + + const allPatterns = (patterns || []) + .map((pattern) => new Minimatch(pattern, {nocomment: true})) + .filter((pattern) => !pattern.empty) + const positivePatterns = allPatterns.filter((pattern) => !pattern.negate) + const negativePatterns = allPatterns.filter((pattern) => pattern.negate) + + return (path: string) => { + if (path) { + const positive = positivePatterns.some((pattern) => pattern.match(path)) + const negative = negativePatterns.some((pattern) => !pattern.match(path)) + return positive && !negative + } + return false + } +} + +export const getCredentialsFile = (cloudOrigin: string, credentialsFile?: string): string => { + if (credentialsFile) { + return credentialsFile + } else { + const url = new URL(cloudOrigin) + const host = url.host + const suffix = host === 'cloud.mobify.com' ? '' : `--${host}` + return path.join(os.homedir(), `.mobify${suffix}`) + } +} + +export const readCredentials = async (filepath: string): Promise => { + try { + const data = await readJson(filepath) + return { + username: data.username, + api_key: data.api_key + } + } catch (e) { + throw new Error( + `Credentials file "${filepath}" not found.\n` + + 'Visit https://runtime.commercecloud.com/account/settings for ' + + 'steps on authorizing your computer to push bundles.' + ) + } +} + +interface LogRecord { + level: string + message: string + shortRequestId?: string +} + +export const parseLog = (log: string): LogRecord => { + const parts = log.trim().split('\t') + let requestId, shortRequestId, level + + if ( + parts.length >= 3 && + validator.isISO8601(parts[0]) && + validator.isUUID(parts[1]) && + validator.isAlpha(parts[2]) + ) { + // An application log + parts.shift() + requestId = parts.shift() + level = parts.shift() + } else { + // A platform log + const words = parts[0].split(' ') + level = words.shift() + parts[0] = words.join(' ') + } + const message = parts.join('\t') + + const match = /(?[a-f\d]{8})/.exec(requestId || message) + if (match) { + shortRequestId = match.groups.id + } + return {level, message, shortRequestId} +} diff --git a/packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/ssr.js b/packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/ssr.js new file mode 100644 index 0000000000..ea4627b1c7 --- /dev/null +++ b/packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/ssr.js @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2021, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +// This file needs to exist as a text fixture, but doesn't need content! diff --git a/packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/static/favicon.ico b/packages/pwa-kit-dev/src/utils/test-fixtures/minimal-built-app/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a15a8f348b8b472b716e4cc5efb3baa760cc4135 GIT binary patch literal 5430 zcmeI0O-NKx6vwYk!!U^G5FrwS6H-%1h(xpqQE4@_Xf{`f(hmp`KWrG_5cILlB2RDf3%M znE)JO{CjX*z9ADJo6W`S(AN(SAOlv{%4OOn6Xf>a+Q43rImF#F(xEZbgUASNjdonU zNn;%6e^8!@K_A#Wb3L1zXxQ|rw{`E%r@r#O_9J^|Ce1$N0q#x|sl43?CA|TD_Y_}x zJCxV^VFI3l?))sA1HCI@zxKWKu-yiF2fl#v&ju;;i~1Ajg}l7uWxH5EwgRj>jNA`{ zum&4`^UE{zpM;~(1)ZS1+R@&S3j^pVKc0pGxB_orJKySChIx1f8F>ndVZ`-Y`zS zPcesSWt_uEgBR06f>PGZ@W7f!`Yp~<}`D-hg3Whpu3aX8_Ge@ zn^soeh3qkpkF<|Nr|y={AmtF`pYICJhk4B4Gb;a-b=E7s_5waG(WcmkiG}WRwdb&e zU)^!V|7{KYYrVY?U=N0nwTQv6dj@qUbO%2Dtu1 z+`}ZSm0=Fgu`vj`*Av*(cc)?KfmZ7O__lFSAKZoQVz7d~>}QIMdrkXU(Dy_ACTnwP zMi%sI{cB#JxB4P{fZRN2|4scJXdSw%CtwsFLmD2z1?UFfzw6LdE@;MI7jgh@z%}Yg zakekHbKUonh;Vi#7MUwgZH_4m7~9` (...args) => { - args[args.length - 1](null, value) -} - -const failureCallbackAdapter = (value = {}) => (...args) => { - args[args.length - 1](value) -} +const successCallbackAdapter = + (value = true) => + (...args) => { + args[args.length - 1](null, value) + } + +const failureCallbackAdapter = + (value = {}) => + (...args) => { + args[args.length - 1](value) + } const argsIgnoringCallback = (mock, index = 0) => mock.mock.calls[index].slice(0, -1) diff --git a/packages/pwa-kit-react-sdk/scripts/setup-jsdom.js b/packages/pwa-kit-react-sdk/scripts/setup-jsdom.js index 58caea1de5..edb29bf1b1 100644 --- a/packages/pwa-kit-react-sdk/scripts/setup-jsdom.js +++ b/packages/pwa-kit-react-sdk/scripts/setup-jsdom.js @@ -9,7 +9,7 @@ global.document = require('jsdom').jsdom('') global.window = document.defaultView global.window.matchMedia = global.window.matchMedia || - function() { + function () { return { matches: false, addListener: () => {}, diff --git a/packages/pwa-kit-react-sdk/scripts/version.js b/packages/pwa-kit-react-sdk/scripts/version.js index 6a9c724dd3..792011c28e 100644 --- a/packages/pwa-kit-react-sdk/scripts/version.js +++ b/packages/pwa-kit-react-sdk/scripts/version.js @@ -11,10 +11,7 @@ const path = require('path') const os = require('os') const fs = require('fs') -const date = new Date() - .toString() - .split(' ') - .slice(1, 4) +const date = new Date().toString().split(' ').slice(1, 4) const heading = `## v${pkg.version} (${date[0]} ${date[1]}, ${date[2]})\n` const changelog = path.resolve(os.tmpdir(), 'CHANGELOG.md') diff --git a/packages/pwa-kit-react-sdk/src/ssr/browser/main.test.js b/packages/pwa-kit-react-sdk/src/ssr/browser/main.test.js index 78ef484b4b..86a095b27b 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/browser/main.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/browser/main.test.js @@ -14,7 +14,7 @@ import AppErrorBoundary from '../universal/components/app-error-boundary' import {uuidv4} from '../../utils/uuidv4.client' jest.mock('../../utils/uuidv4.client') -describe('main', function() { +describe('main', function () { test('OuterApp renders without error', () => { uuidv4.mockReturnValueOnce('7f21aea5-6962-4162-8204-9da85c802022') const oldPreloadedState = window.__PRELOADED_STATE__ diff --git a/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js b/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js index 70874e41b0..aad1ca78e1 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js +++ b/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js @@ -366,6 +366,9 @@ const getWindowProgressive = (req, res) => { } // eslint-disable-next-line no-unused-vars -const serverRenderer = ({clientStats, serverStats}) => (req, res, next) => render(req, res, next) +const serverRenderer = + ({clientStats, serverStats}) => + (req, res, next) => + render(req, res, next) export default serverRenderer diff --git a/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.test.js b/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.test.js index 39eddd3a25..1b0f89f069 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.test.js @@ -359,7 +359,7 @@ jest.mock('@loadable/server', () => { // Tests aren't being run through webpack, therefore no chunks or `loadable-stats.json` // file is being created. ChunkExtractor causes a file read exception. For this // reason, we mock the implementation to do nothing. - ChunkExtractor: function() { + ChunkExtractor: function () { return { collectChunks: jest.fn().mockImplementation((x) => x), getScriptElements: jest.fn().mockReturnValue([]) diff --git a/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.js b/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.js index 9d332a0c31..95328d21fc 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.js +++ b/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.js @@ -222,13 +222,8 @@ export const routeComponent = (Wrapped, isPage, locals) => { } const {location: previousLocation, match: previousMatch} = previousProps - const { - location, - match, - onGetPropsComplete, - onGetPropsError, - onUpdateComplete - } = this.props + const {location, match, onGetPropsComplete, onGetPropsError, onUpdateComplete} = + this.props const {params} = match || {} const {params: previousParams} = previousMatch || {} diff --git a/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.test.js b/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.test.js index 7d2f3566b5..225d6dceb8 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.test.js @@ -16,17 +16,9 @@ const delay = (t) => new Promise((resolve) => setTimeout(resolve, t)) * we want when testing shouldGetProps – always returning true would cause * an infinite loop. */ -const trueOnceThenFalse = () => - jest - .fn() - .mockReturnValue(false) - .mockReturnValueOnce(true) - -const falseOnceThenTrue = () => - jest - .fn() - .mockReturnValue(true) - .mockReturnValueOnce(false) +const trueOnceThenFalse = () => jest.fn().mockReturnValue(false).mockReturnValueOnce(true) + +const falseOnceThenTrue = () => jest.fn().mockReturnValue(true).mockReturnValueOnce(false) jest.mock('../_app-config', () => { const React = require('react') diff --git a/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-legacy-get-props/index.test.js b/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-legacy-get-props/index.test.js index 91fe5e640a..0cfa47f94e 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-legacy-get-props/index.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-legacy-get-props/index.test.js @@ -8,7 +8,7 @@ import {withLegacyGetProps} from './index' import {shallow} from 'enzyme' import React from 'react' -describe('withLegacyGetProps', function() { +describe('withLegacyGetProps', function () { test('Renders correctly', () => { const Wrapped = () =>

Hello world

const Component = withLegacyGetProps(Wrapped) diff --git a/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-react-query/index.test.js b/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-react-query/index.test.js index 28c54b0aac..192650c7db 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-react-query/index.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/universal/components/with-react-query/index.test.js @@ -10,7 +10,7 @@ import React from 'react' import {SERVER_RETRY_WARNING} from '.' -describe('withReactQuery', function() { +describe('withReactQuery', function () { let windowSpy beforeEach(() => { diff --git a/packages/pwa-kit-react-sdk/src/ssr/universal/contexts/index.test.js b/packages/pwa-kit-react-sdk/src/ssr/universal/contexts/index.test.js index 607f93f3b5..bcefc5dc3f 100644 --- a/packages/pwa-kit-react-sdk/src/ssr/universal/contexts/index.test.js +++ b/packages/pwa-kit-react-sdk/src/ssr/universal/contexts/index.test.js @@ -33,7 +33,7 @@ const Component = () => { const {correlationId} = useCorrelationId() return
{correlationId}
} -describe('CorrelationIdProvider', function() { +describe('CorrelationIdProvider', function () { test('Renders without errors', () => { const history = createMemoryHistory() const id = crypto.randomUUID() diff --git a/packages/pwa-kit-runtime/.prettierrc.yaml b/packages/pwa-kit-runtime/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/pwa-kit-runtime/.prettierrc.yaml +++ b/packages/pwa-kit-runtime/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/pwa-kit-runtime/CHANGELOG.md b/packages/pwa-kit-runtime/CHANGELOG.md index 9748c362fa..bf11dd14c2 100644 --- a/packages/pwa-kit-runtime/CHANGELOG.md +++ b/packages/pwa-kit-runtime/CHANGELOG.md @@ -1,4 +1,7 @@ -## v2.6.0-dev (Jan 05, 2023) +## v2.7.0-dev (Jan 25, 2023) +## v2.6.0 (Jan 25, 2023) +- Security package updates + ## v2.5.0 (Jan 05, 2023) - Logging cid from res header isntead of req in local development [#821](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/821) - Replace morgan stream to use console.log [#847](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/847) diff --git a/packages/pwa-kit-runtime/jest.config.js b/packages/pwa-kit-runtime/jest.config.js index 180d456caf..c66c4ddae8 100644 --- a/packages/pwa-kit-runtime/jest.config.js +++ b/packages/pwa-kit-runtime/jest.config.js @@ -10,7 +10,7 @@ module.exports = { ...base, coverageThreshold: { global: { - branches: 92.4, + branches: 90, functions: 87, lines: 90, statements: 90 diff --git a/packages/pwa-kit-runtime/package-lock.json b/packages/pwa-kit-runtime/package-lock.json index bb2d180371..bdcdb16a93 100644 --- a/packages/pwa-kit-runtime/package-lock.json +++ b/packages/pwa-kit-runtime/package-lock.json @@ -1,33 +1,33 @@ { "name": "pwa-kit-runtime", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" } }, "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } @@ -41,12 +41,12 @@ } }, "@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@colors/colors": { @@ -91,7 +91,7 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, "http-errors": { @@ -114,31 +114,31 @@ "dev": true }, "path-to-regexp": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", - "integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", "dev": true }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true } } }, "@loadable/babel-plugin": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@loadable/babel-plugin/-/babel-plugin-5.13.2.tgz", - "integrity": "sha512-vSZUVeTH1S1sDbk8Tzft0plZSkN7W4zmVR5w/Bmy4UmvBiu9lin7ztrDpoUTUzxpoups+OJbTc/OosvN0aMXWg==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/babel-plugin/-/babel-plugin-5.15.3.tgz", + "integrity": "sha512-kwEsPxCk8vnwbTfbA4lHqT5t0u0czCQTnCcmOaTjxT5lCn7yZCBTBa9D7lHs+MLM2WyPsZlee3Qh0TTkMMi5jg==", "requires": { "@babel/plugin-syntax-dynamic-import": "^7.7.4" } }, "@loadable/component": { - "version": "5.15.2", - "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", - "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.3.tgz", + "integrity": "sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==", "dev": true, "requires": { "@babel/runtime": "^7.7.7", @@ -157,9 +157,9 @@ } }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -175,9 +175,9 @@ } }, "@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.3.tgz", + "integrity": "sha512-nhOb2dWPeb1sd3IQXL/dVPnKHDOAFfvichtBf4xV00/rU1QbPCQqKMbvIheIjqwVjh7qIgf2AHTHi391yMOMpQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -186,9 +186,9 @@ } }, "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, "@types/http-proxy": { @@ -200,15 +200,15 @@ } }, "@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "dev": true }, "@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "@types/parse-json": { "version": "4.0.0", @@ -235,20 +235,25 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "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==" + }, "aws-lambda-mock-context": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/aws-lambda-mock-context/-/aws-lambda-mock-context-3.2.1.tgz", @@ -258,12 +263,20 @@ "moment": "^2.10.5", "pinkie-defer": "^1.0.0", "uuid": "^3.0.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "aws-sdk": { - "version": "2.1126.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1126.0.tgz", - "integrity": "sha512-8yeeYFXOwbJIUHIJZlrcgWGvLPi+yayKc2h/+hnNLdnp/u+LUuJqMDH/19fogAai8PANSqI7N9fGkMIBZDrThQ==", + "version": "2.1309.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1309.0.tgz", + "integrity": "sha512-EC/EtDDWDoJnovvmNlJqvojNJMbFZ78HESzgFiond5vzPS+FIWnWgGJ1ZVvIWU9O4X5I8cEMckwHCTfVYNcZxA==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -272,7 +285,8 @@ "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", - "uuid": "3.3.2", + "util": "^0.12.4", + "uuid": "8.0.0", "xml2js": "0.4.19" } }, @@ -316,9 +330,9 @@ "integrity": "sha512-9Kq8m6NZTAgy05Ryuh7U3Qc4/ujLQU1AZ5vMw4cr3igTdi5itZC6kCNrRr2X8NzPiDn2oUIFTfa71DKMnue/Zg==" }, "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -328,7 +342,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -337,7 +351,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "brace-expansion": { "version": "1.1.11", @@ -417,7 +431,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, "color": { @@ -441,7 +455,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "color-string": { "version": "1.9.1", @@ -487,7 +501,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "content-disposition": { "version": "0.5.4", @@ -498,9 +512,9 @@ } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "cookie": { "version": "0.5.0", @@ -510,12 +524,12 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "cookies": { @@ -535,9 +549,9 @@ "dev": true }, "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -601,19 +615,19 @@ "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", "dev": true }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, "depd": { @@ -636,9 +650,9 @@ } }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "dev": true }, "dom-serializer": { @@ -677,7 +691,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "enabled": { "version": "2.0.0", @@ -688,7 +702,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "entities": { "version": "2.2.0", @@ -706,17 +720,17 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "eventemitter3": { "version": "4.0.7", @@ -726,7 +740,7 @@ "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" }, "exec-sh": { "version": "0.2.2", @@ -738,13 +752,13 @@ } }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -763,7 +777,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -831,9 +845,17 @@ "dev": true }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } }, "form-data": { "version": "3.0.1", @@ -860,7 +882,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { "version": "10.1.0", @@ -876,7 +898,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { "version": "1.1.1", @@ -884,28 +906,36 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -923,7 +953,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-symbols": { "version": "1.0.3", @@ -934,7 +964,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -947,7 +976,7 @@ "header-case": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", - "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", + "integrity": "sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==", "requires": { "no-case": "^2.2.0", "upper-case": "^1.1.3" @@ -975,7 +1004,7 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, "http-errors": { @@ -994,7 +1023,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true } } @@ -1036,7 +1065,7 @@ "humanize-number": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/humanize-number/-/humanize-number-0.0.2.tgz", - "integrity": "sha1-EcCvakcWQ2M1iFiASPF5lUFInBg=", + "integrity": "sha512-un3ZAcNQGI7RzaWGZzQDH47HETM4Wrj6z6E4TId8Yeq9w5ZKUVB1nrT2jwFheTUjEmqcgTjXDc959jum+ai1kQ==", "dev": true }, "iconv-lite": { @@ -1064,7 +1093,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -1080,21 +1109,34 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "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==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "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" } @@ -1118,15 +1160,27 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "jmespath": { "version": "0.16.0", @@ -1146,7 +1200,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, "jsonfile": { @@ -1175,9 +1229,9 @@ } }, "koa": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz", - "integrity": "sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.14.1.tgz", + "integrity": "sha512-USJFyZgi2l0wDgqkfD27gL4YGno7TfUkcmOe6UOLFOVuN+J7FwnNu4Dydl4CUQzraM1lBAiGed0M9OVJoT0Kqw==", "dev": true, "requires": { "accepts": "^1.3.5", @@ -1230,7 +1284,7 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true } } @@ -1244,7 +1298,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true } } @@ -1296,19 +1350,13 @@ "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "logform": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", - "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", "dev": true, "requires": { "@colors/colors": "1.5.0", @@ -1329,7 +1377,7 @@ "lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" }, "lru-cache": { "version": "6.0.0", @@ -1342,7 +1390,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "merge": { "version": "1.2.1", @@ -1353,12 +1401,12 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { "version": "4.0.5", @@ -1396,15 +1444,15 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "moment": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", - "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", "dev": true }, "morgan": { @@ -1432,7 +1480,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "negotiator": { "version": "0.6.3", @@ -1445,22 +1493,40 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" }, "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0" + } + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, "path-to-regexp": { @@ -1483,14 +1549,14 @@ } }, "nock": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz", - "integrity": "sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.0.tgz", + "integrity": "sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg==", "dev": true, "requires": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", - "lodash.set": "^4.3.2", + "lodash": "^4.17.21", "propagate": "^2.0.0" }, "dependencies": { @@ -1530,17 +1596,17 @@ } }, "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "requires": { "boolbase": "^1.0.0" } }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, "on-finished": { "version": "2.4.1", @@ -1558,7 +1624,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -1575,7 +1641,7 @@ "only": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", "dev": true }, "parent-module": { @@ -1605,23 +1671,23 @@ "passthrough-counter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passthrough-counter/-/passthrough-counter-1.0.0.tgz", - "integrity": "sha1-GWfZ5m2lcrXAI8eH2xEqOHqxZvo=", + "integrity": "sha512-Wy8PXTLqPAN0oEgBrlnsXPMww3SYJ44tQ8aVrGAI4h4JZYCS0oYqsPqtPR8OhJpv6qFbpbB7XAn0liKV7EXubA==", "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "4.0.0", @@ -1636,7 +1702,7 @@ "pinkie-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/pinkie-defer/-/pinkie-defer-1.0.0.tgz", - "integrity": "sha1-ePzCIRbA2okKySqAitNwiI3tlCE=", + "integrity": "sha512-G5h4CS22qa3Va/diBL7TBcIT3yaQOVSbt6Gbe4/RLpZDdOsg9v3AzUoZBZ99MADMOeq4+pwXQT86wBO0OE7sSA==", "dev": true }, "process-nextick-args": { @@ -1663,12 +1729,12 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } @@ -1676,7 +1742,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "range-parser": { "version": "1.2.1", @@ -1712,15 +1778,15 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "resolve-from": { "version": "4.0.0", @@ -1730,7 +1796,7 @@ "rimraf": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "integrity": "sha512-5QIcndZ8am2WyseL6lln/utl51SwRBQs/at+zi1UnhsnPyZcAID+g0PZrKdb+kJn2fo/CwgyJweR8sP36Jer5g==", "requires": { "glob": "^7.0.5" } @@ -1768,7 +1834,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "requires": { "graceful-fs": "^4.1.6" @@ -1788,9 +1854,9 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-stable-stringify": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", - "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", "dev": true }, "safer-buffer": { @@ -1801,12 +1867,12 @@ "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } @@ -1850,9 +1916,9 @@ } }, "set-cookie-parser": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz", - "integrity": "sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" }, "setprototypeof": { "version": "1.2.0", @@ -1862,7 +1928,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "requires": { "shebang-regex": "^1.0.0" } @@ -1870,7 +1936,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" }, "side-channel": { "version": "1.0.4", @@ -1885,7 +1951,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dev": true, "requires": { "is-arrayish": "^0.3.1" @@ -1933,7 +1999,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true }, "statuses": { @@ -1944,7 +2010,7 @@ "streamsearch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", "dev": true }, "string_decoder": { @@ -2120,7 +2186,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, "triple-beam": { @@ -2151,9 +2217,9 @@ } }, "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" }, "universalify": { "version": "2.0.0", @@ -2164,47 +2230,59 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" }, "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" } }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "watch": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.1.tgz", - "integrity": "sha1-N7f8S3vMr6n2OY3KVqKkVFXbSqI=", + "integrity": "sha512-sNuaD+59FCY61hropnkxHcmTLplnwmxivWguNVlaZeNejCBNS04HsN94uWxGlij1L3Mi4zJBeMoAscufyCdiSw==", "dev": true, "requires": { "exec-sh": "^0.2.0", @@ -2214,7 +2292,7 @@ "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, "whatwg-encoding": { @@ -2228,7 +2306,7 @@ "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "requires": { "tr46": "~0.0.3", @@ -2243,12 +2321,26 @@ "isexe": "^2.0.0" } }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, "winston": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", - "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", "dev": true, "requires": { + "@colors/colors": "1.5.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", @@ -2275,7 +2367,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "xml2js": { "version": "0.4.19", @@ -2289,7 +2381,7 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, "yallist": { "version": "4.0.0", diff --git a/packages/pwa-kit-runtime/package.json b/packages/pwa-kit-runtime/package.json index c8e9630678..f6a1d96d5f 100644 --- a/packages/pwa-kit-runtime/package.json +++ b/packages/pwa-kit-runtime/package.json @@ -1,6 +1,6 @@ { "name": "pwa-kit-runtime", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "description": "The PWAKit Runtime", "repository": { "type": "git", @@ -44,7 +44,7 @@ "rimraf": "2.6.1", "semver": "^7.3.2", "set-cookie-parser": "^2.2.1", - "ua-parser-js": "^0.7.31", + "ua-parser-js": "^0.7.33", "whatwg-encoding": "^1.0.5" }, "devDependencies": { @@ -52,9 +52,9 @@ "@serverless/event-mocks": "^1.1.1", "aws-lambda-mock-context": "^3.2.1", "fs-extra": "^10.1.0", - "internal-lib-build": "^2.6.0-dev", + "internal-lib-build": "^2.7.0-dev", "nock": "^13.1.1", - "node-fetch": "2.6.7", + "node-fetch": "^2.6.7", "s3rver": "^3.1.0", "sinon": "^13.0.1", "superagent": "^6.1.0", @@ -62,7 +62,7 @@ "watch": "1.0.1" }, "peerDependencies": { - "pwa-kit-dev": "^2.6.0-dev" + "pwa-kit-dev": "^2.7.0-dev" }, "peerDependenciesMeta": { "pwa-kit-dev": { diff --git a/packages/pwa-kit-runtime/scripts/version.js b/packages/pwa-kit-runtime/scripts/version.js index 6a9c724dd3..792011c28e 100644 --- a/packages/pwa-kit-runtime/scripts/version.js +++ b/packages/pwa-kit-runtime/scripts/version.js @@ -11,10 +11,7 @@ const path = require('path') const os = require('os') const fs = require('fs') -const date = new Date() - .toString() - .split(' ') - .slice(1, 4) +const date = new Date().toString().split(' ').slice(1, 4) const heading = `## v${pkg.version} (${date[0]} ${date[1]}, ${date[2]})\n` const changelog = path.resolve(os.tmpdir(), 'CHANGELOG.md') diff --git a/packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js b/packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js index 235251fd12..44760afdb5 100644 --- a/packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js +++ b/packages/pwa-kit-runtime/src/ssr/server/build-remote-server.js @@ -185,7 +185,7 @@ export const RemoteServerFactory = { _setupLogging(app) { app.use( expressLogging( - function(tokens, req, res) { + function (tokens, req, res) { const contentLength = tokens.res(req, res, 'content-length') return [ `(${res.locals.requestId})`, @@ -345,14 +345,7 @@ export const RemoteServerFactory = { get applicationCache() { if (!this._applicationCache) { - const bucket = process.env.CACHE_BUCKET_NAME - const useLocalCache = !(isRemote() || bucket) - this._applicationCache = new PersistentCache({ - useLocalCache, - bucket, - prefix: process.env.CACHE_BUCKET_PREFIX, - sendMetric: app.sendMetric.bind(app) - }) + this._applicationCache = new PersistentCache() } return this._applicationCache } @@ -598,10 +591,7 @@ export const RemoteServerFactory = { */ _setupHealthcheck(app) { app.get('/mobify/ping', (_, res) => - res - .set('cache-control', NO_CACHE) - .sendStatus(200) - .end() + res.set('cache-control', NO_CACHE).sendStatus(200).end() ) }, @@ -958,7 +948,7 @@ const prepNonProxyRequest = (req, res, next) => { // to intercept and discard cookie setting. const setHeader = Object.getPrototypeOf(res).setHeader const remote = isRemote() - res.setHeader = function(header, value) { + res.setHeader = function (header, value) { /* istanbul ignore else */ if (header && header.toLowerCase() !== SET_COOKIE && value) { setHeader.call(this, header, value) @@ -1041,7 +1031,7 @@ const applyPatches = once((options) => { // Patch the ExpressJS Response class's redirect function to suppress // the creation of a body (DESKTOP-485). Including the body may // trigger a parsing error in aws-serverless-express. - express.response.redirect = function(status, url) { + express.response.redirect = function (status, url) { let workingStatus = status let workingUrl = url @@ -1054,9 +1044,7 @@ const applyPatches = once((options) => { const address = this.location(workingUrl).get('Location') // Send a minimal response with just a status and location - this.status(workingStatus) - .location(address) - .end() + this.status(workingStatus).location(address).end() } // Patch the whatwg-encoding decode function so that it will accept plain diff --git a/packages/pwa-kit-runtime/src/ssr/server/express.lambda.test.js b/packages/pwa-kit-runtime/src/ssr/server/express.lambda.test.js index 2b0aa5b51b..f71223e3f6 100644 --- a/packages/pwa-kit-runtime/src/ssr/server/express.lambda.test.js +++ b/packages/pwa-kit-runtime/src/ssr/server/express.lambda.test.js @@ -118,9 +118,7 @@ describe('SSRServer Lambda integration', () => { }, route: (req, res) => { // Return a binary payload - res.status(200) - .set('Content-Type', 'image/png') - .send(fakeBinaryPayload) + res.status(200).set('Content-Type', 'image/png').send(fakeBinaryPayload) } }, { @@ -245,12 +243,13 @@ describe('SSRServer Lambda integration', () => { } } - const {handler, app, server: srv} = RemoteServerFactory.createHandler( - options, - (app) => { - app.get('/*', testCase.route) - } - ) + const { + handler, + app, + server: srv + } = RemoteServerFactory.createHandler(options, (app) => { + app.get('/*', testCase.route) + }) server = srv @@ -388,7 +387,11 @@ describe('SSRServer Lambda integration', () => { } } - const {app, handler, server: srv} = RemoteServerFactory.createHandler(options, (app) => { + const { + app, + handler, + server: srv + } = RemoteServerFactory.createHandler(options, (app) => { app.get('/*', route) }) diff --git a/packages/pwa-kit-runtime/src/ssr/server/express.test.js b/packages/pwa-kit-runtime/src/ssr/server/express.test.js index 3ae01964a7..534fcfacb7 100644 --- a/packages/pwa-kit-runtime/src/ssr/server/express.test.js +++ b/packages/pwa-kit-runtime/src/ssr/server/express.test.js @@ -371,18 +371,14 @@ describe('SSRServer operation', () => { test('should not proxy', () => { const app = RemoteServerFactory._createApp(opts()) - return request(app) - .get('/mobify/proxy/base/test/path') - .expect(501) + return request(app).get('/mobify/proxy/base/test/path').expect(501) }) }) test('SSRServer handles /mobify/ping', () => { const app = RemoteServerFactory._createApp(opts()) - return request(app) - .get('/mobify/ping') - .expect(200) + return request(app).get('/mobify/ping').expect(200) }) describe('SSRServer worker.js handling', () => { @@ -431,9 +427,7 @@ describe('SSRServer operation', () => { const app = RemoteServerFactory._createApp(opts({buildDir: tmpDir})) app.get('/worker.js(.map)?', RemoteServerFactory.serveServiceWorker) - return request(app) - .get(requestPath) - .expect(404) + return request(app).get(requestPath).expect(404) }) }) }) @@ -574,9 +568,7 @@ describe('SSRServer operation', () => { app.get('/thing', RemoteServerFactory.serveStaticFile('this-does-not-exist.ico')) - return request(app) - .get('/thing') - .expect(404) + return request(app).get('/thing').expect(404) }) }) @@ -679,7 +671,7 @@ describe('SSRServer persistent caching', () => { 'x-rendered': 'true', 'content-type': 'text/html; charset=utf-8' }, - expectToBeCached: true, + expectToBeCached: false, expectRenderCallCount: 1 }, { @@ -690,7 +682,7 @@ describe('SSRServer persistent caching', () => { 'x-mobify-from-cache': 'false', 'content-type': 'image/png' }, - expectToBeCached: true, + expectToBeCached: false, expectRenderCallCount: 1 }, { @@ -721,18 +713,17 @@ describe('SSRServer persistent caching', () => { url: '/cacheme/?type=html', expectOk: true, expectHeaders: { - 'x-precached': 'true', - 'x-mobify-from-cache': 'true', + 'x-mobify-from-cache': 'false', 'content-type': 'text/html; charset=utf-8' }, - expectToBeCached: true, - expectRenderCallCount: 0, + expectToBeCached: false, + expectRenderCallCount: 1, preCache: { data: Buffer.from('456'), metadata: { status: 200, headers: { - 'x-precached': 'true', + 'x-precached': 'false', 'content-type': 'text/html; charset=utf-8' } } @@ -743,10 +734,10 @@ describe('SSRServer persistent caching', () => { url: '/cacheme/?type=html', expectOk: true, expectHeaders: { - 'x-mobify-from-cache': 'true' + 'x-mobify-from-cache': 'false' }, - expectToBeCached: true, - expectRenderCallCount: 0, + expectToBeCached: false, + expectRenderCallCount: 1, preCache: { data: Buffer.from('123') } @@ -756,11 +747,10 @@ describe('SSRServer persistent caching', () => { url: '/cacheme/?type=none', expectOk: true, expectHeaders: { - 'x-precached': 'true', - 'x-mobify-from-cache': 'true' + 'x-mobify-from-cache': 'false' }, - expectToBeCached: true, - expectRenderCallCount: 0, + expectToBeCached: false, + expectRenderCallCount: 1, preCache: { data: undefined, metadata: { @@ -882,7 +872,7 @@ describe('generateCacheKey', () => { url: '/test?a=1', query: {}, headers: {}, - get: function(key) { + get: function (key) { return this.headers[key] }, ...overrides @@ -1037,3 +1027,36 @@ describe('getRuntime', () => { expect(func()).toBe(MockDevServerFactory.name) }) }) + +describe('DevServer middleware', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + test('_validateConfiguration protocol', () => { + let protocol = 'ftp' + let error = `Invalid local development server protocol ${protocol}. Valid protocols are http and https.` + expect(() => { + RemoteServerFactory._validateConfiguration(opts({protocol})) + }).toThrow(error) + }) + + test('_validateConfiguration sslFilePath', () => { + let sslFilePath = './does/not/exist' + let error = + 'The sslFilePath option passed to the SSR server constructor ' + + 'must be a path to an SSL certificate file ' + + 'in PEM format, whose name ends with ".pem". ' + + 'See the "cert" and "key" options on ' + + 'https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options' + expect(() => { + RemoteServerFactory._validateConfiguration(opts({sslFilePath})) + }).toThrow(error) + }) + test('_validateConfiguration strictSSL', () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}) + RemoteServerFactory._validateConfiguration(opts({strictSSL: false})) + expect(warn.mock.calls).toEqual([ + ['The SSR Server has _strictSSL turned off for https requests'] + ]) + }) +}) diff --git a/packages/pwa-kit-runtime/src/ssr/server/test_fixtures/server-renderer.js b/packages/pwa-kit-runtime/src/ssr/server/test_fixtures/server-renderer.js index 0dd9f50ffe..e2359e98af 100644 --- a/packages/pwa-kit-runtime/src/ssr/server/test_fixtures/server-renderer.js +++ b/packages/pwa-kit-runtime/src/ssr/server/test_fixtures/server-renderer.js @@ -5,4 +5,7 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ // eslint-disable-next-line no-unused-vars -exports.default = ({clientStats, serverStats}) => (req, res, next) => res.send('OK') +exports.default = + ({clientStats, serverStats}) => + (req, res, next) => + res.send('OK') diff --git a/packages/pwa-kit-runtime/src/utils/ssr-cache.js b/packages/pwa-kit-runtime/src/utils/ssr-cache.js index 8fb3a6bb34..4fbf4d6d36 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-cache.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-cache.js @@ -7,27 +7,6 @@ /** * @module progressive-web-sdk/utils/ssr-cache */ -const fs = require('fs') -const os = require('os') -const path = require('path') -const util = require('util') - -const rimraf = require('rimraf') - -// Promisified fs functions -const writeFileAsync = util.promisify(fs.writeFile) -const readFileAsync = util.promisify(fs.readFile) -const unlinkAsync = util.promisify(fs.unlink) - -const nodeRequire = eval('require') - -const ENOENT = 'ENOENT' - -// If we're configured to use S3, we require() the AWS sdk on-demand. -let AWS - -// mS in one year -const ONE_YEAR = 365 * 24 * 3600 * 1000 /** * A cache implementation. @@ -61,164 +40,8 @@ export class PersistentCache { * (name: String, value: Number, unit: String, * dimensions: Object) => undefined */ - constructor({ - useLocalCache, - bucket, - prefix, - s3Endpoint, - accessKeyId, - secretAccessKey, - sendMetric - }) { - this._local = useLocalCache - - if (typeof sendMetric !== 'function') { - throw new Error('sendMetric function must be defined') - } - this._sendMetric = sendMetric - - // In order to prevent a very unlikely possibility that the - // cache delete operation is resolved after the cache put operation. - // We store the cache delete promise and call it in cacheResponseWhenDone - // to make sure we never accidentially put cache and then delete the cache + constructor() { this._cacheDeletePromise = Promise.resolve() - - if (useLocalCache) { - this._internalCachePath = null - this._functional = true - // On the first creation of a local cache, we cleanup - // any stale cache directories - if (!PersistentCache.cleanupDone) { - PersistentCache.cleanupDone = true - PersistentCache._cleanupOnEntry() - } - } else { - // We require the bucket parameter. If it's not present then - // this cache will be non-functional: gets will always return - // not-found, puts and deletes will do nothing. - this._functional = !!bucket - if (this._functional) { - /* istanbul ignore else */ - if (!AWS) { - // Dynamically load the AWS SDK. Because this is a dynamic - // require, webpack will not include the AWS SDK in a built - // Express app file, which reduces the file size. The SDK - // is provided in a Lambda environment. - AWS = nodeRequire('aws-sdk') - } - const s3Params = { - // We support extra debug logging from S3 access via the - // CACHE_LOGGING environment variable. - logger: process.env.CACHE_LOGGING - ? /* istanbul ignore next */ console - : undefined, - region: process.env.AWS_REGION, - endpoint: s3Endpoint, - credentials: accessKeyId - ? new AWS.Credentials(accessKeyId, secretAccessKey) - : /* istanbul ignore next */ - undefined - } - this._s3 = new AWS.S3(s3Params) - this._bucket = bucket - this._prefix = prefix || '' - } - } - } - - /** - * Scan the temporary directory for persistent cache directories. - * For each one we find, check if the owner process is running. If - * not, remove the directory. - * This is only used for local caching. - * @returns {Promise} - * @private - */ - static _cleanupOnEntry() { - return new Promise((resolve, reject) => { - const myPID = process.pid - const nameRE = /^ssr-cache-(\d+)-.*$/ - const tmp = os.tmpdir() - // Scan the temporary directory - fs.readdir(tmp, (err, entries) => { - /* istanbul ignore next */ - if (err) { - console.warning(`Could not check for stale cache directories: ${err}`) - reject(err) - } - entries - // Test each entry with the regex - .map((entry) => nameRE.exec(entry)) - // Filter out any non-matches - .filter((entry) => !!entry) - // Check each match - .forEach((entry) => { - const pid = Number.parseInt(entry[1]) - if (pid !== myPID) { - // See if the process is running. If not, - // we'll clean up this directory - try { - process.kill(pid, 0) - } catch (err) { - /* istanbul ignore else */ - if (err.code === 'ESRCH') { - // The process does not exist; clean up - rimraf.sync(path.join(tmp, entry[0])) - } else { - /* istanbul ignore next */ - reject(err) - } - } - } - }) - - resolve() - }) - }) - } - - /** - * Cleanup handler for local cache directory - * @private - */ - _localCacheCleanup() { - if (this._internalCachePath) { - rimraf.sync(this._internalCachePath) - this._internalCachePath = null - } - } - - /** - * Given a process pid, return an absolute base cache path for it, in a form - * suitable for passing to fs.mkdtemp - * Exposed for testing. - * @param pid {Number} - * @returns {String} - * @private - */ - static _cachePathForPID(pid) { - // The name format used here must match the RE used in - // the _cleanupOnEntry method above. - return `${os.tmpdir()}${path.sep}ssr-cache-${pid}-` - } - - /** - * Return the local cache path. If there is no local cache directory, - * create and and return the path. - * - * This method will also arrange for the cache directory to be - * cleaned up on process exit. - * - * @returns {String} the cache path, ending in '/' - * @private - */ - get _cachePath() { - if (!this._internalCachePath) { - const cacheDir = fs.mkdtempSync(PersistentCache._cachePathForPID(process.pid)) - this._internalCachePath = `${cacheDir}${path.sep}` - } - - return this._internalCachePath } /** @@ -226,98 +49,7 @@ export class PersistentCache { * clean up any locally cached data. * @private */ - close() { - this._localCacheCleanup() - } - - /** - * Given a key and a namespace, convert them to safe forms. We do - * this because there are restrictions on the characters that can make up - * namespaces and keys, and those restrictions are different between local - * and remote cache implementations. Converting the strings to safe forms - * means that any characters can be used in keys, without the caller - * having to care about restrictions. - * - * The key is composed by a path and a hashed object name, because we need - * to hash the query strings to prevent unexpected characters in file path. - * The namespace is made safe for use as a directory name. If the - * namespace is falsy (the default), it's returned as an empty - * string. If it is non-falsy, it's returned with a trailing slash - * so that it can be used as a directory name. - * - * @param key {String} the cache key - * @param namespace {String|string[]} the cache namespace - * @returns {Object} with safeKey and safeNamespace properties - * @returns {Object} result the processed values - * @returns {string} result.safeKey the key (which is a hash of the input) - * @returns {string} result.safeNamespace the namespace as a path string, - * ending in a slash - * @returns {string} result.separator the path separator value ('/' for - * posix, '\' for Windows) - * @returns {string} result.s3Key the full S3 Key value (including the - * namespace and any S3 prefix) - * @returns {string} result.s3DataKey the full S3 key for the data part - * of the cache entry - * @returns {string} result.s3MetadataKey the full S3 key for the metadata - * part of the cache entry - * @private - */ - _sanitize(key, namespace) { - let safeNamespace - - // Use the correct separator to join elements into a path - const separator = this._local ? path.sep : '/' - - const keyElements = key.split('/') - // safeKey is the last part of generated cache key - // it is the hashed value - const safeKey = keyElements.pop() - - // For each element of the namespace, convert slashes, whitespace, - // asterisks to underscore, then join the namespace elements. - const workingNamespace = [] - - // Include any prefix as the leading path element. This is only - // relevant for S3. - if (this._prefix) { - workingNamespace.push(this._prefix) - } - - if (typeof namespace === 'string') { - workingNamespace.push(namespace) - } else if (namespace != null) { - workingNamespace.push(...namespace) - } - - workingNamespace.push(...keyElements) - - // eslint-disable-next-line - const cleanupRE = /[\s\/*\\]/gi - // Build safeNamespace as a path, ending in a path separator, so that - // it will work as both a local and S3 directory path. Any falsy - // elements in the namespace Array are ignored.. - safeNamespace = - workingNamespace - // Remove empty or undefined elements - .filter((element) => !!element) - // Clean up element characters - .map((element) => element.replace(cleanupRE, '_').trim()) - // Join together as a directory path, ending with a slash - .join(separator) + separator - - const s3Key = safeNamespace === '/' ? safeKey : safeNamespace + safeKey - - return { - safeKey, - safeNamespace, - // Provided for testing - separator, - // S3-specific - s3Key, - s3DataKey: `${s3Key}.data`, - s3MetadataKey: `${s3Key}.metadata` - } - } + close() {} /** * Return an object that represents an entry not found in the cache @@ -336,179 +68,6 @@ export class PersistentCache { } } - /** - * Local cache getter - * - * @param key {String} the cache key - * @param namespace {String|string[]} the cache namespace - * @private - */ - _localGetter(key, namespace) { - const {safeKey, safeNamespace} = this._sanitize(key, namespace) - const filePath = path.join(this._cachePath, safeNamespace, safeKey) - - return readFileAsync(filePath, this._fileOptions) - .then((fileData) => { - // Filedata is a Buffer at this point. - const metaDataLength = fileData.readUInt16LE(0) - const metadata = JSON.parse(fileData.slice(2, metaDataLength + 2).toString()) - const data = fileData.slice(metaDataLength + 2) - return { - metadata, - data: data.length ? data : undefined - } - }) - .catch((err) => { - /* istanbul ignore else */ - if (err.code === ENOENT) { - return null - } else { - /* istanbul ignore next */ - throw err - } - }) - .then((cacheData) => { - if (!cacheData) { - return this._notFound(key, namespace) - } - - const {data, metadata} = cacheData - - // Check for expiration. - const expiration = metadata.expiration - if (expiration < Date.now()) { - console.log(` - Application Cache ${metadata.key} expired, deleting cache object. - `) - this._cacheDeletePromise = this._localDeleter(key, namespace) - return this._notFound(key, namespace) - } - - // If isJSON is set, deserialize the data (which is a Buffer), - // otherwise return it as a Buffer. - return { - key: metadata.key, - namespace: metadata.namespace, - found: true, - metadata: metadata.metadata, - data: data && metadata.isJSON ? JSON.parse(data.toString()) : data, - expiration - } - }) - } - - /** - * Wrapper for s3.getObject that returns a Promise. If the operation - * rejects, the rejection is caught and logged, and null is returned. - * - * @param params {Object} S3 getObject params - * @returns Promise - * @private - */ - _s3SafeGet(params) { - return this._s3 - .getObject(params) - .promise() - .catch((err) => { - // We don't expect errors, so we'll log - /* istanbul ignore next */ - if (err.code !== 'NoSuchKey') { - console.log( - `Unexpected error ${err} from s3.getObject for ${JSON.stringify(params)}` - ) - } - return null - }) - } - - /** - * Remote cache getter - * - * @param key {String} the cache key - * @param namespace {String} the cache namespace - * @private - */ - _remoteGetter(key, namespace) { - const {s3DataKey, s3MetadataKey} = this._sanitize(key, namespace) - - const dataGetParams = { - Bucket: this._bucket, - Key: s3DataKey - } - const metadataGetParams = { - Bucket: this._bucket, - Key: s3MetadataKey - } - - // Fetch both parts of the entry in parallel - return Promise.all([this._s3SafeGet(dataGetParams), this._s3SafeGet(metadataGetParams)]) - .then(([dataResult, metadataResult]) => { - // If either object was not read, we consider - // that we did not find an entry. - if (!dataResult || !metadataResult) { - return this._notFound(key, namespace) - } - - const objectMetadata = JSON.parse(metadataResult.Body.toString()) - - // S3 package does not implement automatic expiration, - // so we store the expiration timestamp in the - // objectMetadata and test it here. - const expiration = objectMetadata.expiration - if (expiration && expiration < Date.now()) { - console.log(` - Application Cache ${objectMetadata.key} expired, deleting cache object. - `) - this._cacheDeletePromise = this._remoteDeleter(key, namespace) - return this._notFound(key, namespace) - } - - // If isJSON is set, deserialize the data (which is a Buffer), - // otherwise return it as a Buffer. - let data = dataResult.Body - if (objectMetadata.bodyIsEmpty) { - data = undefined - } else if (objectMetadata.isJSON) { - data = JSON.parse(data.toString()) - } - - return { - data, - expiration, - key: objectMetadata.key, - namespace: objectMetadata.namespace, - metadata: objectMetadata.metadata, - found: true - } - }) - .catch((err) => /* istanbul ignore next */ { - console.log(`Unexpected error ${err} processing cache entry`) - return this._notFound(key, namespace) - }) - } - - /** - * Internal implementation of get function. - * - * Function is separated into its own function so that - * execution time can be measured for metric purposes - * - * @param [namespace] {String|string[]} the cache namespace - * @param key {String} the cache key - * @returns {Promise<*>} A Promise that will resolve to the - * cache result, or null if there is no match in the cache. - * @private - */ - _internalGet({key, namespace}) { - if (this._functional) { - return this._local - ? this._localGetter(key, namespace) - : this._remoteGetter(key, namespace) - } else { - return Promise.resolve(this._notFound(key, namespace)) - } - } - /** * Get a JavaScript object from the cache. * @@ -543,180 +102,7 @@ export class PersistentCache { * cache result, or null if there is no match in the cache. */ get({key, namespace}) { - const _metricName = (entry) => { - return entry.found - ? 'ApplicationCacheRetrievalTimeHit' - : 'ApplicationCacheRetrievalTimeMiss' - } - - return this._calculateExecutionMetrics( - this._internalGet.bind(this, {key, namespace}), - _metricName - ).then((entry) => { - this._sendMetric('ApplicationCacheHitOccurred', entry.found ? 1 : 0, 'None') - return entry - }) - } - - /** - * Local cache setter - * - * @param key {String} cache key - * @param namespace {String} the cache namespace - * @param data {Buffer} data to be cached - * @param [metadata] {Object} metadata for the cache entry - * @param expiration {Number} expiration time as a JS date/timestamp - * @param isJSON {Boolean} true if data is serialized JSON - * @private - */ - _localSetter(key, namespace, data, metadata, expiration, isJSON) { - const {safeKey, safeNamespace, separator} = this._sanitize(key, namespace) - - const metaDataAsBuffer = Buffer.from( - JSON.stringify({expiration, isJSON, key, namespace, metadata}) - ) - const metaDataLength = Buffer.alloc(2) - metaDataLength.writeUInt16LE(metaDataAsBuffer.length, 0) - - const toCache = [metaDataLength, metaDataAsBuffer] - // Track the total length of the buffers; this halves the time needed - // to perform Buffer.concat below. - let totalLength = metaDataLength.length + metaDataAsBuffer.length - if (data) { - toCache.push(data) - totalLength += data.length - } - - const fileData = Buffer.concat(toCache, totalLength) - - // Verify that the full path exists, creating it if - // it doesn't. - const dirPath = safeNamespace.split(separator).reduce((currentPath, element) => { - currentPath = path.join(currentPath, element) - if (!fs.existsSync(currentPath)) { - fs.mkdirSync(currentPath) - } - return currentPath - }, this._cachePath) - const filePath = path.join(dirPath, safeKey) - - return writeFileAsync(filePath, fileData) - } - - /** - * Remote cache setter - * - * @param key {String} cache key - * @param namespace {String} the cache namespace - * @param data {Buffer} data to be cached - * @param [metadata] {Object} any metadata to be stored with the - * cache entry - * @param expiration {Number} expiration time as a JS date/timestamp - * @param isJSON {Boolean} true if data is serialized JSON - * @private - */ - _remoteSetter(key, namespace, data, metadata, expiration, isJSON) { - const {s3DataKey, s3MetadataKey} = this._sanitize(key, namespace) - const bodyIsEmpty = !data - - /* - A brief explanation of metadata. - The 'metadata' parameter to this function is an (optional) - JSON-serializable object that the caller wants to be stored - along with the data. - The 'objectMetadata' constructed below is the metadata that the - PersistentCache class needs to store along with the data. It - contains the 'metadata'. - Although S3 supports per-object metadata, it's quite severely - limited. Because it's intended to support adding headers to S3 - objects served over HTTP, there are tight constraints on the - characters allowed. Also, once metadata has been set for an - object, it can't be updated (the object must be copied and the - old object deleted). We therefore store the metadata in a - separate S3 object alongside the data. - */ - const objectMetadata = { - isJSON, - expiration, - key, - namespace, - metadata, - bodyIsEmpty - } - const expirationAsDate = new Date(expiration) - const dataPutParams = { - Bucket: this._bucket, - Key: s3DataKey, - // We cannot store an undefined body, we must pass - // some value. - Body: bodyIsEmpty ? '' : data, - Expires: expirationAsDate - } - const metadataPutParams = { - Bucket: this._bucket, - Key: s3MetadataKey, - // We stringify the metadata so that it's more human- - // readable, to aid debugging. - Body: JSON.stringify(objectMetadata, null, 2), - Expires: expirationAsDate - } - - // We do both PUT operations in parallel. If either fails, - // the Promise.all will reject, which might leave one object - // written but the other one missing. Both need to exist - // for the cache entry to be found by the _remoteGetter. - return Promise.all([ - this._s3.putObject(dataPutParams).promise(), - this._s3.putObject(metadataPutParams).promise() - ]) - } - - /** - * Internal implementation of put function. - * - * Function is separated into its own function so that - * execution time can be measured for metric purposes - * - * @param key {String} the cache key. - * @param [namespace] {String|string[]} the cache namespace - * @param data {Buffer|*} the data to be stored. - * @param [metadata] {Object} a simple JS object with keys and values - * for metadata. This object MUST be JSON-seralizable. - * @param [expiration] {Number} the expiration date/time for the data, - * as a JS date/timestamp (the result of Date.getTime). If the expiration - * is less than PersistentCache.DELTA_THRESHOLD (midnight on January 1st, 1980) - * it is interpreted as a delta number of mS to be added to the current time. - * This allows for deltas up to ten years. - * @returns {Promise<*>} resolves when data has been stored, or rejects - * on an error - * @private - */ - _internalPut({key, namespace, data, metadata, expiration}) { - if (!this._functional) { - return Promise.resolve() - } - - let workingData = data - let isJSON = false - - // Serialize anything that isn't a Buffer - if (data && !(data instanceof Buffer)) { - workingData = Buffer.from(JSON.stringify(data)) - isJSON = true - } - - // If there is no expiration, set it one year into the future - let workingExpiration = expiration || ONE_YEAR - // If the expiration value is less than DELTA_THRESHOLD, - // consider it as a delta, otherwise consider it absolute. - const expirationTimestamp = - workingExpiration < PersistentCache.DELTA_THRESHOLD - ? Date.now() + workingExpiration - : workingExpiration - - return this._local - ? this._localSetter(key, namespace, workingData, metadata, expirationTimestamp, isJSON) - : this._remoteSetter(key, namespace, workingData, metadata, expirationTimestamp, isJSON) + return Promise.resolve(this._notFound(key, namespace)) } /** @@ -760,56 +146,8 @@ export class PersistentCache { * @returns {Promise<*>} resolves when data has been stored, or rejects * on an error */ - put({key, namespace, data, metadata, expiration}) { - return this._calculateExecutionMetrics( - this._internalPut.bind(this, {key, namespace, data, metadata, expiration}), - () => 'ApplicationCacheStorageTime' - ) - } - - /** - * Local cache deleter - * - * @private - */ - _localDeleter(key, namespace) { - const {safeKey, safeNamespace} = this._sanitize(key, namespace) - const filePath = path.join(this._cachePath, safeNamespace, safeKey) - - return unlinkAsync(filePath).catch((err) => { - /* istanbul ignore else */ - if (err.code === ENOENT) { - return null - } - /* istanbul ignore next */ - throw err - }) - } - - _remoteDeleter(key, namespace) { - const {s3DataKey, s3MetadataKey} = this._sanitize(key, namespace) - - const dataDeleteParams = { - Bucket: this._bucket, - Key: s3DataKey - } - const metadataDeleteParams = { - Bucket: this._bucket, - Key: s3MetadataKey - } - - // Delete both objects in parallel - return Promise.all([ - this._s3.deleteObject(dataDeleteParams).promise(), - this._s3.deleteObject(metadataDeleteParams).promise() - ]).catch((err) => { - // We do not expect to get errors, so we log. Note - // that we cannot tell which of the promises rejected. - /* istanbul ignore next */ - if (err.code !== 'NoSuchKey') { - console.log(`Unexpected error ${err} from s3.deleteObject`) - } - }) + put() { + return Promise.resolve() } /** @@ -818,27 +156,8 @@ export class PersistentCache { * @param [namespace] {String|string[]} the cache namespace * @returns {Promise.<*>} resolves when delete is complete */ - delete({key, namespace}) { - if (!this._functional) { - return Promise.resolve() - } - - return this._local - ? this._localDeleter(key, namespace) - : this._remoteDeleter(key, namespace) - } - - _calculateExecutionMetrics(functionToTimeExecution, metricNameFunc) { - const putTimeStart = new Date() - - return functionToTimeExecution().then((returnObject) => { - this._sendMetric( - metricNameFunc(returnObject), - new Date() - putTimeStart, - 'Milliseconds' - ) - return returnObject - }) + delete() { + return Promise.resolve() } } diff --git a/packages/pwa-kit-runtime/src/utils/ssr-cache.test.js b/packages/pwa-kit-runtime/src/utils/ssr-cache.test.js index 6fcf20d281..11e1abb7f5 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-cache.test.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-cache.test.js @@ -4,506 +4,51 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -const fs = require('fs') -const os = require('os') -const path = require('path') - -const S3rver = require('s3rver') -const sinon = require('sinon') - import {PersistentCache} from './ssr-cache' -const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) - const localRemoteTestCases = [true, false] -const namespaceTestCases = [undefined, 'second'] -/* - This set of tests loops over using local and remote caches, and for each - cache type, loops over two namespaces. Most tests start with a 'get' of - the key used in the test, which verifies that keys from one namespace - don't affect tests performed in a separate namespace. - */ localRemoteTestCases.forEach((useLocalCache) => { - const nameBase = `${useLocalCache ? 'Local' : 'Remote'} cache` - - describe(nameBase, () => { - const sandbox = sinon.createSandbox() - const sendMetric = sandbox.stub() + const name = `${useLocalCache ? 'Local' : 'Remote'} noop PersistentCache` + describe(name, () => { const testCache = new PersistentCache({ useLocalCache, bucket: 'TestBucket', s3Endpoint: 'http://localhost:4568', accessKeyId: 'S3RVER', secretAccessKey: 'S3RVER', - sendMetric: sendMetric - }) - - let mockS3Directory - let s3 - - beforeAll((done) => { - if (!useLocalCache) { - mockS3Directory = fs.mkdtempSync(path.join(os.tmpdir(), 'mockS3')) - s3 = new S3rver({ - directory: mockS3Directory, - resetOnClose: true, - // Set this to false to get more verbose logging - // from the S3 emulator - silent: true, - configureBuckets: [ - { - name: 'TestBucket' - } - ] - }) - return s3.run(done) - } else { - done() - } + sendMetric: () => {} }) - - afterAll(() => { - if (!useLocalCache) { - if (s3) { - s3.close() - s3 = undefined - } - fs.rmdirSync(mockS3Directory) - mockS3Directory = undefined - } - }) - - namespaceTestCases.forEach((namespace) => { - const name = `${namespace || 'default'} namespace,` - - describe(name, () => { - test(`${name} set/get buffer`, () => { - const buf = Buffer.alloc(128) - for (let i = 0; i <= 128; i++) { - buf[i] = i - } - - const key = 'abc' - const expiration = Date.now() + 10000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, data: buf, expiration}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result).toBeDefined() - expect(result.found).toBe(true) - expect(result.expiration).toEqual(expiration) - expect(result.key).toEqual(key) - expect(result.metadata).toBeUndefined() - const buf2 = result.data - expect(buf2).toBeDefined() - expect(buf2.equals(buf)).toBe(true) - }) - }) - - test(`${name} set/get JSON`, () => { - const data = { - a: 1, - b: '2', - c: { - d: 'abcd' - }, - e: [1, 2, 3, 4] - } - const metadata = { - x: 'x' - } - - const key = 'xyz' - const expiration = Date.now() + 10000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, data, metadata, expiration}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result).toBeDefined() - expect(result.found).toBe(true) - expect(result.expiration).toEqual(expiration) - expect(result.key).toEqual(key) - expect(result.data).toEqual(data) - expect(result.metadata).toEqual(metadata) - }) - }) - - test(`${name} set/get metadata only`, () => { - const metadata = { - xyz: '123' - } - - const key = 'pqr' - const expiration = Date.now() + 10000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, metadata, expiration}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result).toBeDefined() - expect(result.found).toBe(true) - expect(result.expiration).toEqual(expiration) - expect(result.key).toEqual(key) - expect(result.data).toBeUndefined() - expect(result.metadata).toEqual(metadata) - }) - }) - - test(`${name} set/get empty`, () => { - const key = 'stu' - const expiration = Date.now() + 10000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, expiration}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result).toBeDefined() - expect(result.found).toBe(true) - expect(result.expiration).toEqual(expiration) - expect(result.key).toEqual(key) - expect(result.data).toBeUndefined() - expect(result.metadata).toBe(undefined) - }) - }) - - test(`${name} set with default expiration`, () => { - const key = 'jkl' - const oneYear = Date.now() + 365 * 24 * 3600 * 1000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, data: '123'}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result).toBeDefined() - expect(result.found).toBe(true) - expect(Math.abs(result.expiration - oneYear)).toBeLessThan(500) - expect(result.data).toEqual('123') - expect(result.key).toEqual(key) - }) - }) - - test(`${name} delete existing`, () => { - const key = 'ghi' - const expiration = Date.now() + 10000 - - return testCache - .get({key, namespace}) - .then((result) => { - expect(result.data).toBeUndefined() - expect(result.metadata).toBeUndefined() - expect(result.found).toBe(false) - expect(result.key).toEqual(key) - expect(result.namespace).toEqual(namespace) - return testCache.put({key, namespace, data: '0123456789', expiration}) - }) - .then(() => testCache.get({key, namespace})) - .then((result) => expect(result).toBeDefined()) - .then(() => testCache.delete({key, namespace})) - .then(() => testCache.get({key, namespace})) - .then((result) => expect(result.found).toBe(false)) - .then(() => testCache.delete({key, namespace})) - .then(() => testCache.get({key, namespace})) - .then((result) => expect(result.found).toBe(false)) - }) - - test(`${name} delete non-existing`, () => { - const key = 'nosuchkey' - return testCache - .get({key, namespace}) - .then((result) => expect(result.found).toBe(false)) - .then(() => testCache.delete({key, namespace})) - .then(() => testCache.get({key, namespace})) - .then((result) => expect(result.found).toBe(false)) - }) - - test(`${name} absolute expiration`, () => { - const key = 'def1' - const expiration = Date.now() + 250 - - return testCache - .put({key, namespace, data: '12345', expiration}) - .then(() => sleep(500)) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result.found).toBe(false) - expect(result.data).toBeUndefined() - }) - }) - - test(`${name} delta expiration`, () => { - const key = 'def2' - const expiration = 250 - - return testCache - .put({key, namespace, data: '12345', expiration}) - .then(() => sleep(500)) - .then(() => testCache.get({key, namespace})) - .then((result) => { - expect(result.found).toBe(false) - expect(result.data).toBeUndefined() - }) - }) - }) - }) - - test('sanitize returns expected value', () => { - const key = '/test/1/55c6759ac7fc71bf306199ad6fa407a1b5bd6ff2a2611d178b9add32f1cf4062' - const namespace = 'namespace' - if (useLocalCache) { - expect(testCache._sanitize(key, namespace).s3Key).toBe( - `${namespace}${key}`.replace(/\//g, path.sep) - ) - expect(testCache._sanitize(key, namespace).safeNamespace).toBe( - 'namespace/test/1/'.replace(/\//g, path.sep) - ) - expect(testCache._sanitize(key).s3Key).toBe( - key.substring(1).replace(/\//g, path.sep) - ) - expect(testCache._sanitize(key).safeNamespace).toBe( - 'test/1/'.replace(/\//g, path.sep) - ) - } else { - expect(testCache._sanitize(key, namespace).s3Key).toBe(`${namespace}${key}`) - expect(testCache._sanitize(key, namespace).safeNamespace).toBe('namespace/test/1/') - expect(testCache._sanitize(key).s3Key).toBe(key.substring(1)) - expect(testCache._sanitize(key).safeNamespace).toBe('test/1/') - } - }) - - test('namespace construction', () => { - let {safeNamespace, separator} = testCache._sanitize('x', 'ns') - expect(safeNamespace).toEqual(`ns${separator}`) - - safeNamespace = testCache._sanitize('x', ['ns']).safeNamespace - expect(safeNamespace).toEqual(`ns${separator}`) - - safeNamespace = testCache._sanitize('x', [undefined, 'ns1', '', 'ns2']).safeNamespace - expect(safeNamespace).toEqual(`ns1${separator}ns2${separator}`) - }) - - test('namespaces are distinct', () => { - const key = 'mno' - - const ns1 = ['a', '1'] - const ns2 = ['a', '2'] - - return Promise.all([ - testCache.get({key, namespace: ns1}), - testCache.get({key, namespace: ns2}) - ]) - .then(([one, two]) => { - expect(one.data).toBeUndefined() - expect(one.namespace).toEqual(ns1) - expect(two.data).toBeUndefined() - expect(two.namespace).toEqual(ns2) - return Promise.all([ - testCache.put({key, namespace: ns1, data: 'one'}), - testCache.put({key, namespace: ns2, data: 'two'}) - ]) - }) - .then(() => - Promise.all([ - testCache.get({key, namespace: ns1}), - testCache.get({key, namespace: ns2}) - ]) - ) - .then(([one, two]) => { - expect(one.data).toEqual('one') - expect(one.namespace).toEqual(ns1) - expect(two.data).toEqual('two') - expect(two.namespace).toEqual(ns2) - }) - }) - }) -}) - -describe('Remote cache specific tests', () => { - const sandbox = sinon.createSandbox() - - test('non-functional', () => { - const cache = new PersistentCache({useLocalCache: false, sendMetric: sandbox.stub()}) - expect(cache._functional).toBe(false) - - return cache - .get({key: 'k'}) - .then((result) => { - expect(result.found).toBe(false) - return cache.put({key: 'k', data: '123'}) - }) - .then(() => cache.get({key: 'k'})) - .then((result) => { + const key = 'key' + const namespace = 'namespace' + const buf = Buffer.alloc(8) + for (let i = 0; i <= 8; i++) { + buf[i] = i + } + const expiration = Date.now() + 10000 + test('get', () => { + testCache.get({key, namespace}).then((result) => { + expect(result.data).toBeUndefined() + expect(result.metadata).toBeUndefined() expect(result.found).toBe(false) - return cache.delete({key: 'k'}) + expect(result.key).toEqual(key) + expect(result.namespace).toEqual(namespace) }) - }) - - test('prefix', () => { - const cache = new PersistentCache({ - useLocalCache: false, - prefix: 'abc', - bucket: 'TestBucket', - sendMetric: sandbox.stub() }) - expect(cache._functional).toBe(true) - - const key = 'xyzzy' - const namespace = 'plugh' - const {s3Key, safeNamespace} = cache._sanitize(key, namespace) - expect(safeNamespace).toEqual('abc/plugh/') - expect(s3Key.startsWith(safeNamespace)).toBe(true) - }) -}) - -describe('Metrics Sending tests', () => { - const sandbox = sinon.createSandbox() - const namespace = 'test123' - - afterEach(() => { - sandbox.restore() - }) - - test('Sending Metrics for Cache Miss', () => { - const sendMetric = sandbox.stub() - const cache = new PersistentCache({useLocalCache: true, sendMetric: sendMetric}) - - const data = { - a: 1, - b: '2', - c: { - d: 'abcd' - }, - e: [1, 2, 3, 4] - } - const metadata = { - x: 'x' - } - - const key = 'xyz' - const expiration = Date.now() + 10000 - - return cache.get({key, namespace}).then(() => { - expect(sendMetric.calledWith('ApplicationCacheRetrievalTimeHit')).toBe(false) - expect(sendMetric.calledWith('ApplicationCacheRetrievalTimeMiss')).toBe(true) - expect(sendMetric.calledWith('ApplicationCacheHitOccurred', 0)).toBe(true) - return cache.put({key, namespace, data, metadata, expiration}) + test('put', () => { + testCache + .put({key, namespace, data: buf, expiration}) + .then(() => testCache.get({key, namespace})) + .then((result) => { + expect(result.data).toBeUndefined() + expect(result.metadata).toBeUndefined() + expect(result.found).toBe(false) + expect(result.key).toEqual(key) + expect(result.namespace).toEqual(namespace) + }) }) - }) - - test('Sending Metrics for Cache Storage and Retrival', () => { - const sendMetric = sandbox.stub() - const cache = new PersistentCache({useLocalCache: true, sendMetric: sendMetric}) - - const data = { - a: 1, - b: '2', - c: { - d: 'abcd' - }, - e: [1, 2, 3, 4] - } - const metadata = { - x: 'x' - } - - const key = 'xyz' - const expiration = Date.now() + 10000 - - return cache - .put({key, namespace, data, metadata, expiration}) - .then(() => { - expect(sendMetric.calledWith('ApplicationCacheStorageTime')).toBe(true) - return cache.get({key, namespace}) - }) - .then(() => { - expect(sendMetric.calledWith('ApplicationCacheRetrievalTimeHit')).toBe(true) - expect(sendMetric.calledWith('ApplicationCacheRetrievalTimeMiss')).toBe(false) - expect(sendMetric.calledWith('ApplicationCacheHitOccurred', 1)).toBe(true) - }) - }) - - test('Error thrown when sendMetrics not a function ', () => { - const sendMetric = {a: 1} - - expect(() => { - new PersistentCache({useLocalCache: true, sendMetric: sendMetric}) - }).toThrow() - }) - - test('Error thrown when sendMetrics not undefined ', () => { - expect(() => { - new PersistentCache({useLocalCache: true}) - }).toThrow() - }) -}) - -describe('Local cache specific tests', () => { - const sandbox = sinon.createSandbox() - - test('Cache cleaned correctly', () => { - // First, create a cache directory for a process that will - // never exist - const fakeDir = fs.mkdtempSync(PersistentCache._cachePathForPID(999999)) - - expect(fs.existsSync(fakeDir)).toBe(true) - - // Next, create a cache, and make sure it has a directory - const cache = new PersistentCache({useLocalCache: true, sendMetric: sandbox.stub()}) - const cacheDir = cache._cachePath - expect(fs.existsSync(cacheDir)).toBe(true) - - // Run the cleanup logic. We expect this to clean up - // the fake directory, but leave the cache's directory. - return PersistentCache._cleanupOnEntry().then(() => { - // Check the results - expect(fs.existsSync(fakeDir)).toBe(false) - expect(fs.existsSync(cacheDir)).toBe(true) - - // Close the cache and check that it cleaned up - cache.close() - expect(fs.existsSync(cacheDir)).toBe(false) + test('delete', async () => { + await expect(testCache.delete({key, namespace})).resolves.not.toThrow() }) }) }) diff --git a/packages/pwa-kit-runtime/src/utils/ssr-proxying.js b/packages/pwa-kit-runtime/src/utils/ssr-proxying.js index d0c4929e03..9b15bcb141 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-proxying.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-proxying.js @@ -603,9 +603,8 @@ export const rewriteProxyResponseHeaders = ({ // Even though API Gateway has a header value limit of // 10240 bytes, we choose to limit the length of the header // value to 8192 bytes. - const fullRequestUrl = (requestUrl.startsWith('/') - ? `${targetOrigin}${requestUrl}` - : requestUrl + const fullRequestUrl = ( + requestUrl.startsWith('/') ? `${targetOrigin}${requestUrl}` : requestUrl ).slice(0, MAX_URL_LENGTH_BYTES) logging && console.log( diff --git a/packages/pwa-kit-runtime/src/utils/ssr-proxying.test.js b/packages/pwa-kit-runtime/src/utils/ssr-proxying.test.js index 4e07a57fe4..2d480e8b44 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-proxying.test.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-proxying.test.js @@ -268,8 +268,7 @@ describe('rewriteProxyResponseHeaders tests', () => { 'set-cookie': [ { key: 'Set-Cookie', - value: - 'origin_dc=war; expires=Mon, 01-Oct-2018 00:13:20 GMT; path=/; domain=.www.customer.com' + value: 'origin_dc=war; expires=Mon, 01-Oct-2018 00:13:20 GMT; path=/; domain=.www.customer.com' } ] }, @@ -277,8 +276,7 @@ describe('rewriteProxyResponseHeaders tests', () => { 'set-cookie': [ { key: 'Set-Cookie', - value: - 'origin_dc=war; Path=/; Expires=Mon, 01 Oct 2018 00:13:20 GMT; Domain=apphost.mobify.com' + value: 'origin_dc=war; Path=/; Expires=Mon, 01 Oct 2018 00:13:20 GMT; Domain=apphost.mobify.com' } ] } diff --git a/packages/pwa-kit-runtime/src/utils/ssr-server.test.js b/packages/pwa-kit-runtime/src/utils/ssr-server.test.js index 887430f795..3c15b0d18d 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-server.test.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-server.test.js @@ -81,26 +81,20 @@ const userAgents = { 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', iphoneX: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', - nexus4: - 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', - nexus5: - 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', - nexus6: - 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', + nexus4: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', + nexus5: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', + nexus6: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', galaxyS5: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', - pixel2: - 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', + pixel2: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36', pixel2XL: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Mobile Safari/537.36' }, tablet: { - nexus7: - 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Safari/537.36', + nexus7: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Safari/537.36', nexus10: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3598.0 Safari/537.36', - ipad: - 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1', + ipad: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1', ipadAir: 'Mozilla/5.0 (iPad; CPU OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1', ipadPro: @@ -113,8 +107,7 @@ const userAgents = { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:63.0) Gecko/20100101 Firefox/63.0', safari11: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15', - edge17: - 'Mozilla/5.0 (Windows NT 10.0; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134', + edge17: 'Mozilla/5.0 (Windows NT 10.0; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134', ie11: 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' } } diff --git a/packages/pwa-kit-runtime/src/utils/ssr-server/outgoing-request-hook.js b/packages/pwa-kit-runtime/src/utils/ssr-server/outgoing-request-hook.js index 18e9bc65f1..8eeae67211 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-server/outgoing-request-hook.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-server/outgoing-request-hook.js @@ -32,7 +32,7 @@ const getKeepAliveAgents = () => { } export const outgoingRequestHook = (wrapped, options) => { - return function() { + return function () { // Get the app hostname. If we can't, then just pass // the call through to the wrapped function. We'll also // do that if there's no access key. diff --git a/packages/pwa-kit-runtime/src/utils/ssr-server/update-global-agent-options.test.js b/packages/pwa-kit-runtime/src/utils/ssr-server/update-global-agent-options.test.js new file mode 100644 index 0000000000..4309194a6d --- /dev/null +++ b/packages/pwa-kit-runtime/src/utils/ssr-server/update-global-agent-options.test.js @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023, Salesforce, Inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import {updateGlobalAgentOptions, AGENT_OPTIONS_TO_COPY} from './update-global-agent-options' + +describe('update-global-agent-options', () => { + test('non copy option', () => { + let from = {options: {random: 'value'}} + let to = {options: {}} + AGENT_OPTIONS_TO_COPY.forEach((key) => { + from.options[key] = key + }) + updateGlobalAgentOptions(from, to) + expect(Object.keys(to.options).length).toEqual(AGENT_OPTIONS_TO_COPY.length) + expect(to.options).not.toHaveProperty('random') + }) +}) diff --git a/packages/pwa-kit-runtime/src/utils/ssr-server/utils.test.js b/packages/pwa-kit-runtime/src/utils/ssr-server/utils.test.js index c7a1db1ab9..9344010b1d 100644 --- a/packages/pwa-kit-runtime/src/utils/ssr-server/utils.test.js +++ b/packages/pwa-kit-runtime/src/utils/ssr-server/utils.test.js @@ -60,3 +60,11 @@ describe.each([[true], [false]])('Utils remote/local tests (isRemote: %p)', (isR }) }) }) + +describe('catchAndLog', () => { + test('error', () => { + const error = jest.spyOn(console, 'error').mockImplementation(() => {}) + utils.catchAndLog() + expect(error).toHaveBeenCalledWith('Uncaught exception: ', '(no error)') + }) +}) diff --git a/packages/template-express-minimal/.prettierrc.yaml b/packages/template-express-minimal/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/template-express-minimal/.prettierrc.yaml +++ b/packages/template-express-minimal/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/template-express-minimal/app/ssr.test.js b/packages/template-express-minimal/app/ssr.test.js index a104ce1a9c..b137eb2e27 100644 --- a/packages/template-express-minimal/app/ssr.test.js +++ b/packages/template-express-minimal/app/ssr.test.js @@ -10,9 +10,6 @@ const app = require('./ssr') describe('server', () => { afterAll(() => app.server.close()) test('responds with HTML', () => { - return request(app.server) - .get('/') - .expect(200) - .expect('Content-Type', /html/) + return request(app.server).get('/').expect(200).expect('Content-Type', /html/) }) }) diff --git a/packages/template-express-minimal/package-lock.json b/packages/template-express-minimal/package-lock.json index 4c7e634a5f..cf91fbe19d 100644 --- a/packages/template-express-minimal/package-lock.json +++ b/packages/template-express-minimal/package-lock.json @@ -1,13 +1,13 @@ { "name": "template-express-minimal", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "call-bind": { @@ -36,9 +36,9 @@ "dev": true }, "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "core-util-is": { @@ -47,10 +47,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "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" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "extend": { @@ -83,14 +92,14 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "has": { @@ -117,13 +126,13 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "mime": { @@ -148,15 +157,15 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "process-nextick-args": { @@ -166,9 +175,9 @@ "dev": true }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "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" @@ -231,17 +240,6 @@ "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" - } - } } }, "supertest": { @@ -257,7 +255,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true } } diff --git a/packages/template-express-minimal/package.json b/packages/template-express-minimal/package.json index 301e5f5bed..b0f23f0a89 100644 --- a/packages/template-express-minimal/package.json +++ b/packages/template-express-minimal/package.json @@ -1,11 +1,11 @@ { "name": "template-express-minimal", - "version": "2.6.0-dev", + "version": "2.7.0-dev", "license": "See license in LICENSE", "private": true, "devDependencies": { - "pwa-kit-dev": "^2.6.0-dev", - "pwa-kit-runtime": "^2.6.0-dev", + "pwa-kit-dev": "^2.7.0-dev", + "pwa-kit-runtime": "^2.7.0-dev", "supertest": "^4.0.2" }, "scripts": { diff --git a/packages/template-retail-react-app/.prettierrc.yaml b/packages/template-retail-react-app/.prettierrc.yaml index 45ca9af994..33069bf2b2 100644 --- a/packages/template-retail-react-app/.prettierrc.yaml +++ b/packages/template-retail-react-app/.prettierrc.yaml @@ -4,3 +4,4 @@ semi: false bracketSpacing: false tabWidth: 4 arrowParens: 'always' +trailingComma: 'none' diff --git a/packages/template-retail-react-app/CHANGELOG.md b/packages/template-retail-react-app/CHANGELOG.md index fbd4fba8c1..f97a28cf2e 100644 --- a/packages/template-retail-react-app/CHANGELOG.md +++ b/packages/template-retail-react-app/CHANGELOG.md @@ -1,3 +1,11 @@ +## v2.6.0 (Jan 25, 2023) +- Mega menu fixes [#875](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/875) and [#910](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/910) +- Cache SLAS callback using request processor [#884](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/884) +- Fix padding of footer drawer component [#899](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/899) +- Update createOrder to send SLAS USID [#920](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/920) +- Fix PropTypes [#924](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/924) +- Remove unnecessary map statement [#929](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/929) + ## v2.5.0 (Jan 5, 2023) - Add instanceType to Einstein activity body [#858](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/858) - Do not use a proxy to call Einstein [#857](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/857) diff --git a/packages/template-retail-react-app/app/commerce-api/auth.js b/packages/template-retail-react-app/app/commerce-api/auth.js index f60523b3fa..b505848208 100644 --- a/packages/template-retail-react-app/app/commerce-api/auth.js +++ b/packages/template-retail-react-app/app/commerce-api/auth.js @@ -257,14 +257,8 @@ class Auth { * @param {object} tokenResponse - access_token,id_token,refresh_token, expires_in,token_type, usid, customer_id, enc_user_id, idp_access_token */ _handleShopperLoginTokenResponse(tokenResponse) { - const { - access_token, - refresh_token, - customer_id, - usid, - enc_user_id, - id_token - } = tokenResponse + const {access_token, refresh_token, customer_id, usid, enc_user_id, id_token} = + tokenResponse this.authToken = `Bearer ${access_token}` this.usid = usid this.cid = customer_id diff --git a/packages/template-retail-react-app/app/commerce-api/einstein.test.js b/packages/template-retail-react-app/app/commerce-api/einstein.test.js index 46568b83b6..81eacf6ed7 100644 --- a/packages/template-retail-react-app/app/commerce-api/einstein.test.js +++ b/packages/template-retail-react-app/app/commerce-api/einstein.test.js @@ -57,8 +57,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"product":{"id":"56736828M","sku":"56736828M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"product":{"id":"56736828M","sku":"56736828M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -74,8 +73,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"searchText":"tie","products":[{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},{"id":"25752235M","sku":"25752235M","altId":"","altIdType":""},{"id":"25752218M","sku":"25752218M","altId":"","altIdType":""},{"id":"25752981M","sku":"25752981M","altId":"","altIdType":""}],"showProducts":true,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"searchText":"tie","products":[{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},{"id":"25752235M","sku":"25752235M","altId":"","altIdType":""},{"id":"25752218M","sku":"25752218M","altId":"","altIdType":""},{"id":"25752981M","sku":"25752981M","altId":"","altIdType":""}],"showProducts":true,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -90,8 +88,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"category":{"id":"mens-accessories-ties"},"products":[{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},{"id":"25752235M","sku":"25752235M","altId":"","altIdType":""},{"id":"25752218M","sku":"25752218M","altId":"","altIdType":""},{"id":"25752981M","sku":"25752981M","altId":"","altIdType":""}],"showProducts":true,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"category":{"id":"mens-accessories-ties"},"products":[{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},{"id":"25752235M","sku":"25752235M","altId":"","altIdType":""},{"id":"25752218M","sku":"25752218M","altId":"","altIdType":""},{"id":"25752981M","sku":"25752981M","altId":"","altIdType":""}],"showProducts":true,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -108,8 +105,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"searchText":"tie","product":{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"searchText":"tie","product":{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -125,8 +121,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"category":{"id":"mens-accessories-ties"},"product":{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"category":{"id":"mens-accessories-ties"},"product":{"id":"25752986M","sku":"25752986M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -142,8 +137,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"currentLocation":"/","cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"currentLocation":"/","cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -158,8 +152,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"products":[{"id":"682875719029M","sku":"","price":29.99,"quantity":1}],"amount":29.99,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"products":[{"id":"682875719029M","sku":"","price":29.99,"quantity":1}],"amount":29.99,"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -176,8 +169,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"stepName":"CheckoutStep","stepNumber":0,"basketId":"f6bbeee30fb93c2f94213f60f8","cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"stepName":"CheckoutStep","stepNumber":0,"basketId":"f6bbeee30fb93c2f94213f60f8","cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -192,8 +184,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"products":[{"id":"883360544021M","sku":"","price":155,"quantity":1}],"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"products":[{"id":"883360544021M","sku":"","price":155,"quantity":1}],"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -208,8 +199,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"recommenderName":"testRecommender","__recoUUID":"883360544021M","product":{"id":"56736828M","sku":"56736828M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"recommenderName":"testRecommender","__recoUUID":"883360544021M","product":{"id":"56736828M","sku":"56736828M","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) @@ -224,8 +214,7 @@ describe('EinsteinAPI', () => { 'Content-Type': 'application/json', 'x-cq-client-id': 'test-id' }, - body: - '{"recommenderName":"testRecommender","__recoUUID":"883360544021M","products":{"id":"test-reco"},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' + body: '{"recommenderName":"testRecommender","__recoUUID":"883360544021M","products":{"id":"test-reco"},"cookieId":"test-usid","realm":"test","instanceType":"sbx"}' } ) }) diff --git a/packages/template-retail-react-app/app/commerce-api/hooks/useBasket.js b/packages/template-retail-react-app/app/commerce-api/hooks/useBasket.js index eccf6c9d5c..0dd23e1bb1 100644 --- a/packages/template-retail-react-app/app/commerce-api/hooks/useBasket.js +++ b/packages/template-retail-react-app/app/commerce-api/hooks/useBasket.js @@ -365,6 +365,11 @@ export default function useBasket(opts = {}) { */ async createOrder() { const response = await api.shopperOrders.createOrder({ + // We send the SLAS usid via this header. This is required by ECOM to map + // Einstein events sent via the API with the finishOrder event fired by ECOM + // when an Order transitions from Created to New status. + // Without this, various order conversion metrics will not appear on reports and dashboards + headers: {_sfdc_customer_id: api.auth.usid}, body: {basketId: basket.basketId} }) diff --git a/packages/template-retail-react-app/app/commerce-api/hooks/useCustomer.js b/packages/template-retail-react-app/app/commerce-api/hooks/useCustomer.js index e7d9b67f11..4404d86260 100644 --- a/packages/template-retail-react-app/app/commerce-api/hooks/useCustomer.js +++ b/packages/template-retail-react-app/app/commerce-api/hooks/useCustomer.js @@ -118,7 +118,6 @@ export default function useCustomer() { } const response = await api.shopperCustomers.registerCustomer({body}) - // Check for error json response if (response.detail && response.title && response.type) { throw new Error(response.detail) diff --git a/packages/template-retail-react-app/app/commerce-api/index.js b/packages/template-retail-react-app/app/commerce-api/index.js index 325ce70f72..4e6ea114ac 100644 --- a/packages/template-retail-react-app/app/commerce-api/index.js +++ b/packages/template-retail-react-app/app/commerce-api/index.js @@ -107,7 +107,7 @@ class CommerceAPI { self._sdkInstances = { ...self._sdkInstances, [key]: new Proxy(new SdkClass(this._config), { - get: function(obj, prop) { + get: function (obj, prop) { if (typeof obj[prop] === 'function') { return (...args) => { const fetchOptions = args[0] diff --git a/packages/template-retail-react-app/app/commerce-api/index.test.js b/packages/template-retail-react-app/app/commerce-api/index.test.js index d53e998d63..a37bec8b81 100644 --- a/packages/template-retail-react-app/app/commerce-api/index.test.js +++ b/packages/template-retail-react-app/app/commerce-api/index.test.js @@ -27,6 +27,13 @@ import { jest.mock('cross-fetch', () => jest.requireActual('jest-fetch-mock')) +jest.mock('./utils', () => { + const originalModule = jest.requireActual('./utils') + return { + ...originalModule + } +}) + const apiConfig = { ...appConfig.commerceAPI, einsteinConfig: appConfig.einsteinAPI, @@ -678,6 +685,7 @@ describe('CommerceAPI', () => { const api = getAPI() fetch.mockResponseOnce(JSON.stringify(ocapiBasketResponse)) const response = await api.shopperOrders.createOrder({ + headers: {_sfdc_customer_id: 'usid'}, parameters: {}, body: {basketId: ''} }) @@ -688,6 +696,7 @@ describe('CommerceAPI', () => { const api = getAPI() fetch.mockResponseOnce(JSON.stringify(ocapiBasketResponse)) const response = await api.shopperOrders.createOrder({ + headers: {_sfdc_customer_id: 'usid'}, parameters: {} }) expect(response.title).toEqual('Body is required for this request') @@ -697,6 +706,7 @@ describe('CommerceAPI', () => { const api = getAPI() fetch.mockResponseOnce(JSON.stringify(ocapiBasketResponse)) const response = await api.shopperOrders.getOrder({ + headers: {_sfdc_customer_id: 'usid'}, parameters: {orderNo: ''} }) expect(response).toBeDefined() @@ -706,6 +716,7 @@ describe('CommerceAPI', () => { const api = getAPI() fetch.mockResponseOnce(JSON.stringify(ocapiBasketResponse)) const response = await api.shopperOrders.getOrder({ + headers: {_sfdc_customer_id: 'usid'}, parameters: {} }) expect(response.title).toEqual( @@ -721,6 +732,7 @@ describe('CommerceAPI', () => { await expect( api.shopperOrders.createOrder({ parameters: {}, + headers: {_sfdc_customer_id: 'usid'}, body: {basketId: ''} }) ).rejects.toThrow(ocapiFaultResponse.fault.message) diff --git a/packages/template-retail-react-app/app/commerce-api/mock-data.js b/packages/template-retail-react-app/app/commerce-api/mock-data.js index 2fbd57db0a..4b5fac4426 100644 --- a/packages/template-retail-react-app/app/commerce-api/mock-data.js +++ b/packages/template-retail-react-app/app/commerce-api/mock-data.js @@ -133,8 +133,7 @@ export const ocapiBasketWithItem = { merchandize_total_tax: 0.75, notes: { _type: 'simple_link', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/6d0ef818e2b431645b844e6ac2/notes' + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/6d0ef818e2b431645b844e6ac2/notes' }, order_total: null, product_items: [ @@ -267,8 +266,7 @@ export const ocapiBasketWithPaymentInstrumentAndBillingAddress = { merchandize_total_tax: 0.75, notes: { _type: 'simple_link', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/6d0ef818e2b431645b844e6ac2/notes' + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/6d0ef818e2b431645b844e6ac2/notes' }, order_total: null, payment_instruments: [ @@ -399,8 +397,7 @@ export const mockShippingMethods = { shipping_promotions: [ { callout_msg: 'Free Shipping Amount Above 150', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/promotions/3184d71eea54c9d27e88dc41ca', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/promotions/3184d71eea54c9d27e88dc41ca', promotion_id: 'FreeShippingAmountAbove150', promotion_name: 'Free Shipping Amount Above 150' } @@ -782,16 +779,14 @@ export const productsResponse = { alt: 'Long Sleeve Crew Neck, Fire Red, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -813,16 +808,14 @@ export const productsResponse = { alt: 'Long Sleeve Crew Neck, Fire Red, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -844,16 +837,14 @@ export const productsResponse = { alt: 'Long Sleeve Crew Neck, Fire Red, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -875,8 +866,7 @@ export const productsResponse = { alt: 'Long Sleeve Crew Neck, Fire Red, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -1811,16 +1801,14 @@ export const mockOrderProducts = { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6dc64ae1/images/large/PG.10201818.JJ1ANXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6dc64ae1/images/large/PG.10201818.JJ1ANXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6dc64ae1/images/large/PG.10201818.JJ1ANXX.PZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' }, { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6c256020/images/large/PG.10201818.JJ1ANXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6c256020/images/large/PG.10201818.JJ1ANXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6c256020/images/large/PG.10201818.JJ1ANXX.BZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' } ], @@ -1842,16 +1830,14 @@ export const mockOrderProducts = { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad2ef842/images/medium/PG.10201818.JJ1ANXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad2ef842/images/medium/PG.10201818.JJ1ANXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad2ef842/images/medium/PG.10201818.JJ1ANXX.PZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' }, { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwca64fee1/images/medium/PG.10201818.JJ1ANXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwca64fee1/images/medium/PG.10201818.JJ1ANXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwca64fee1/images/medium/PG.10201818.JJ1ANXX.BZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' } ], @@ -1873,16 +1859,14 @@ export const mockOrderProducts = { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2ecc9f4b/images/small/PG.10201818.JJ1ANXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2ecc9f4b/images/small/PG.10201818.JJ1ANXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2ecc9f4b/images/small/PG.10201818.JJ1ANXX.PZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' }, { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc4b732e9/images/small/PG.10201818.JJ1ANXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc4b732e9/images/small/PG.10201818.JJ1ANXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc4b732e9/images/small/PG.10201818.JJ1ANXX.BZ.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' } ], @@ -1904,8 +1888,7 @@ export const mockOrderProducts = { alt: 'Pleated Bib Long Sleeve Shirt, Silver Grey, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc39b144c/images/swatch/PG.10201818.JJ1ANXX.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc39b144c/images/swatch/PG.10201818.JJ1ANXX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc39b144c/images/swatch/PG.10201818.JJ1ANXX.CP.jpg', title: 'Pleated Bib Long Sleeve Shirt, Silver Grey' } ], @@ -2057,16 +2040,14 @@ export const mockOrderProducts = { alt: 'Long Sleeve Crew Neck, Fire Red, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3ce02e8b/images/large/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdc28ed23/images/large/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -2088,16 +2069,14 @@ export const mockOrderProducts = { alt: 'Long Sleeve Crew Neck, Fire Red, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8434410d/images/medium/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc50f7b16/images/medium/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -2119,16 +2098,14 @@ export const mockOrderProducts = { alt: 'Long Sleeve Crew Neck, Fire Red, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8173d41b/images/small/PG.10219685.JJ825XX.PZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' }, { alt: 'Long Sleeve Crew Neck, Fire Red, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw426088e2/images/small/PG.10219685.JJ825XX.BZ.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -2150,8 +2127,7 @@ export const mockOrderProducts = { alt: 'Long Sleeve Crew Neck, Fire Red, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbc8a4ed/images/swatch/PG.10219685.JJ825XX.CP.jpg', title: 'Long Sleeve Crew Neck, Fire Red' } ], @@ -2666,8 +2642,7 @@ export const mockedCustomerProductListsDetails = { alt: 'Apple iPod Nano, Green, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-electronics-m-catalog/default/dw26470cbd/images/large/ipod-nano-green.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw26470cbd/images/large/ipod-nano-green.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw26470cbd/images/large/ipod-nano-green.jpg', title: 'Apple iPod Nano, Green' } ], @@ -2689,8 +2664,7 @@ export const mockedCustomerProductListsDetails = { alt: 'Apple iPod Nano, Green, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-electronics-m-catalog/default/dw610652b7/images/medium/ipod-nano-green.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw610652b7/images/medium/ipod-nano-green.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw610652b7/images/medium/ipod-nano-green.jpg', title: 'Apple iPod Nano, Green' } ], @@ -2712,8 +2686,7 @@ export const mockedCustomerProductListsDetails = { alt: 'Apple iPod Nano, Green, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-electronics-m-catalog/default/dw09f0fd49/images/small/ipod-nano-green.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw09f0fd49/images/small/ipod-nano-green.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw09f0fd49/images/small/ipod-nano-green.jpg', title: 'Apple iPod Nano, Green' } ], @@ -2735,8 +2708,7 @@ export const mockedCustomerProductListsDetails = { alt: 'Apple iPod Nano, Green, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-electronics-m-catalog/default/dw849cd37f/images/swatch/green.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw849cd37f/images/swatch/green.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-electronics-m-catalog/default/dw849cd37f/images/swatch/green.jpg', title: 'Apple iPod Nano, Green' } ], @@ -3053,33 +3025,22 @@ export const mockedCustomerProductListsDetails = { } export const mockCategories = { - 'mens-clothing-jackets': { - id: 'mens-clothing-suits', - image: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-en/default/dw56b28e03/images/slot/sub_banners/cat-banner-mens-suits.jpg', - name: 'Suits', - pageDescription: - "Shop Men's suits for business or pleasure. Enjoy from a variety of different styles and cuts at Commerce Cloud.", - pageTitle: 'Mens Suits for Business and Casual', - parentCategoryId: 'mens-clothing', - parentCategoryTree: [ + root: { + categories: [ { id: 'mens', - name: 'Mens' - }, - { - id: 'mens-clothing', - name: 'Clothing' - }, - { - id: 'mens-clothing-suits', - name: 'Suits' + name: 'Mens', + pageDescription: + "Men's range. Hard-wearing boots, jackets and clothing for unbeatable comfort day in, day out. Practical, easy-to-wear styles wherever you're headed.", + pageKeywords: 'mens boots, mens shoes, mens clothing, mens apparel, mens jackets', + pageTitle: "Men's Footwear, Outerwear, Clothing & Accessories", + parentCategoryId: 'root', + c_showInMenu: true, + image: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-en/default/dw56b28e03/images/slot/sub_banners/cat-banner-mens-suits.jpg' } ], - c_enableCompare: false, - c_showInMenu: true, - c_slotBannerImage: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-en/default/dw2ec92167/images/slot/landing/cat-landing-slotbottom-mens-suits.jpg' + id: 'root', + name: 'Storefront Catalog - Non-EN' } } @@ -3093,8 +3054,7 @@ export const mockProductSearch = { alt: 'Slim Skirt With Back Kick Pleats, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c5f9222/images/large/PG.10221626.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c5f9222/images/large/PG.10221626.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c5f9222/images/large/PG.10221626.JJ3WCXX.PZ.jpg', title: 'Slim Skirt With Back Kick Pleats, ' }, orderable: true, @@ -3192,8 +3152,7 @@ export const mockProductSearch = { alt: 'Pull On Neutral Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw22e88fa3/images/large/PG.10224484.JJ0CZXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw22e88fa3/images/large/PG.10224484.JJ0CZXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw22e88fa3/images/large/PG.10224484.JJ0CZXX.PZ.jpg', title: 'Pull On Neutral Pant, ' }, orderable: true, @@ -3275,8 +3234,7 @@ export const mockProductSearch = { alt: 'Washable Linen Slim Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwab413b1b/images/large/PG.10243116.JJ2DGXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwab413b1b/images/large/PG.10243116.JJ2DGXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwab413b1b/images/large/PG.10243116.JJ2DGXX.PZ.jpg', title: 'Washable Linen Slim Pant, ' }, orderable: true, @@ -3374,8 +3332,7 @@ export const mockProductSearch = { alt: 'Classic Crop Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbce5c490/images/large/PG.10232332.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbce5c490/images/large/PG.10232332.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbce5c490/images/large/PG.10232332.JJ169XX.PZ.jpg', title: 'Classic Crop Pant, ' }, orderable: true, @@ -3473,8 +3430,7 @@ export const mockProductSearch = { alt: 'Slim Floral Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwea207073/images/large/PG.10225656.JJ3WDXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwea207073/images/large/PG.10225656.JJ3WDXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwea207073/images/large/PG.10225656.JJ3WDXX.PZ.jpg', title: 'Slim Floral Skirt, ' }, orderable: true, @@ -3572,8 +3528,7 @@ export const mockProductSearch = { alt: 'Trouser Leg Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw56926d5f/images/large/PG.10207088.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw56926d5f/images/large/PG.10207088.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw56926d5f/images/large/PG.10207088.JJ169XX.PZ.jpg', title: 'Trouser Leg Pant, ' }, orderable: true, @@ -3671,8 +3626,7 @@ export const mockProductSearch = { alt: 'Tribal Inspired Slim Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw0ecd95d1/images/large/PG.10244521.JJ3WDXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw0ecd95d1/images/large/PG.10244521.JJ3WDXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw0ecd95d1/images/large/PG.10244521.JJ3WDXX.PZ.jpg', title: 'Tribal Inspired Slim Skirt, ' }, orderable: true, @@ -3754,8 +3708,7 @@ export const mockProductSearch = { alt: 'Relaxed Fit Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4eee8da9/images/large/PG.10228765.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4eee8da9/images/large/PG.10228765.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4eee8da9/images/large/PG.10228765.JJ169XX.PZ.jpg', title: 'Relaxed Fit Pant, ' }, orderable: true, @@ -3837,8 +3790,7 @@ export const mockProductSearch = { alt: '5 Pocket Cuffed Capri, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb4de158c/images/large/PG.60113984.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb4de158c/images/large/PG.60113984.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb4de158c/images/large/PG.60113984.JJ169XX.PZ.jpg', title: '5 Pocket Cuffed Capri, ' }, orderable: true, @@ -3936,8 +3888,7 @@ export const mockProductSearch = { alt: 'Pleated Skirt., , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf573a20b/images/large/PG.10255878.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf573a20b/images/large/PG.10255878.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf573a20b/images/large/PG.10255878.JJ3WCXX.PZ.jpg', title: 'Pleated Skirt., ' }, orderable: true, @@ -4011,8 +3962,7 @@ export const mockProductSearch = { alt: 'Classic Glen Plaid Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d0a44b0/images/large/PG.10233641.JJ3WRXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d0a44b0/images/large/PG.10233641.JJ3WRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d0a44b0/images/large/PG.10233641.JJ3WRXX.PZ.jpg', title: 'Classic Glen Plaid Pant, ' }, orderable: true, @@ -4094,8 +4044,7 @@ export const mockProductSearch = { alt: 'Long Pleated Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw26c8763f/images/large/PG.10239110.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw26c8763f/images/large/PG.10239110.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw26c8763f/images/large/PG.10239110.JJ3WCXX.PZ.jpg', title: 'Long Pleated Skirt, ' }, orderable: true, @@ -4161,8 +4110,7 @@ export const mockProductSearch = { alt: 'Pleated Skirt With Embroidery., , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw07b70fb1/images/large/PG.10245262.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw07b70fb1/images/large/PG.10245262.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw07b70fb1/images/large/PG.10245262.JJ3WCXX.PZ.jpg', title: 'Pleated Skirt With Embroidery., ' }, orderable: true, @@ -4260,8 +4208,7 @@ export const mockProductSearch = { alt: 'Straight Ankle Pant., , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdaccb91d/images/large/PG.10246102.JJ0DDXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdaccb91d/images/large/PG.10246102.JJ0DDXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdaccb91d/images/large/PG.10246102.JJ0DDXX.PZ.jpg', title: 'Straight Ankle Pant., ' }, orderable: true, @@ -4359,8 +4306,7 @@ export const mockProductSearch = { alt: 'Classic Tweed Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8ae5dac1/images/large/PG.10208897.JJ0QRXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8ae5dac1/images/large/PG.10208897.JJ0QRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8ae5dac1/images/large/PG.10208897.JJ0QRXX.PZ.jpg', title: 'Classic Tweed Pant, ' }, orderable: true, @@ -4458,8 +4404,7 @@ export const mockProductSearch = { alt: 'Ribbed Pleated Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa7953e0e/images/large/PG.10218193.JJ0NLE5.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa7953e0e/images/large/PG.10218193.JJ0NLE5.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa7953e0e/images/large/PG.10218193.JJ0NLE5.PZ.jpg', title: 'Ribbed Pleated Skirt, ' }, orderable: true, @@ -4533,8 +4478,7 @@ export const mockProductSearch = { alt: 'Bootleg Trouser, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8641f0b8/images/large/PG.10204133.JJ0CZXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8641f0b8/images/large/PG.10204133.JJ0CZXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8641f0b8/images/large/PG.10204133.JJ0CZXX.PZ.jpg', title: 'Bootleg Trouser, ' }, orderable: true, @@ -4624,8 +4568,7 @@ export const mockProductSearch = { alt: 'Wide Leg Pant., , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2c21e32c/images/large/PG.10245233.JJ3WDXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2c21e32c/images/large/PG.10245233.JJ3WDXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2c21e32c/images/large/PG.10245233.JJ3WDXX.PZ.jpg', title: 'Wide Leg Pant., ' }, orderable: true, @@ -4723,8 +4666,7 @@ export const mockProductSearch = { alt: 'Eyelet Skirt., , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9efe5554/images/large/PG.10253481.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9efe5554/images/large/PG.10253481.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9efe5554/images/large/PG.10253481.JJ169XX.PZ.jpg', title: 'Eyelet Skirt., ' }, orderable: true, @@ -4822,8 +4764,7 @@ export const mockProductSearch = { alt: 'Washable Wool Classic Straight Skirt , , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf6272a34/images/large/PG.10176157.JJ5AJXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf6272a34/images/large/PG.10176157.JJ5AJXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf6272a34/images/large/PG.10176157.JJ5AJXX.PZ.jpg', title: 'Washable Wool Classic Straight Skirt , ' }, orderable: true, @@ -4921,8 +4862,7 @@ export const mockProductSearch = { alt: 'Roll Up Cargo Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9f11c8e4/images/large/PG.10224067.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9f11c8e4/images/large/PG.10224067.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9f11c8e4/images/large/PG.10224067.JJ169XX.PZ.jpg', title: 'Roll Up Cargo Pant, ' }, orderable: true, @@ -5020,8 +4960,7 @@ export const mockProductSearch = { alt: 'Side Button Pleated Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw05ea84dc/images/large/PG.10227169.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw05ea84dc/images/large/PG.10227169.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw05ea84dc/images/large/PG.10227169.JJ3WCXX.PZ.jpg', title: 'Side Button Pleated Skirt, ' }, orderable: true, @@ -5079,8 +5018,7 @@ export const mockProductSearch = { alt: 'Flat Front Pant, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e830b49/images/large/PG.10240166.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e830b49/images/large/PG.10240166.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e830b49/images/large/PG.10240166.JJ3WCXX.PZ.jpg', title: 'Flat Front Pant, ' }, orderable: true, @@ -5178,8 +5116,7 @@ export const mockProductSearch = { alt: 'Pencil Skirt, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw900cf60d/images/large/PG.10235198.JJ3WCXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw900cf60d/images/large/PG.10235198.JJ3WCXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw900cf60d/images/large/PG.10235198.JJ3WCXX.PZ.jpg', title: 'Pencil Skirt, ' }, orderable: true, @@ -5277,8 +5214,7 @@ export const mockProductSearch = { alt: 'Light Weight Cargo Capri (Master), , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe7549446/images/large/PG.60114143.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe7549446/images/large/PG.60114143.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe7549446/images/large/PG.60114143.JJ169XX.PZ.jpg', title: 'Light Weight Cargo Capri (Master), ' }, orderable: true, @@ -5716,8 +5652,7 @@ export const mockProductSearch = { export const mockCategory = { id: 'mens-accessories-ties', - image: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-non-en/default/dwd2ff3ec8/images/slot/sub_banners/cat-banner-mens-ties.jpg', + image: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-non-en/default/dwd2ff3ec8/images/slot/sub_banners/cat-banner-mens-ties.jpg', name: 'Ties', pageDescription: "Shop Mens's Ties for all occasions including business or casual at Commerce Cloud", @@ -5831,16 +5766,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, ' }, { alt: 'Checked Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, ' } ], @@ -5852,16 +5785,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Cobalt, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, Cobalt' }, { alt: 'Checked Silk Tie, Cobalt, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1abd7d2f/images/large/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, Cobalt' } ], @@ -5883,16 +5814,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Navy, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5170f98a/images/large/PG.949612424S.NAVYSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5170f98a/images/large/PG.949612424S.NAVYSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5170f98a/images/large/PG.949612424S.NAVYSI.PZ.jpg', title: 'Checked Silk Tie, Navy' }, { alt: 'Checked Silk Tie, Navy, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfdc619bd/images/large/PG.949612424S.NAVYSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfdc619bd/images/large/PG.949612424S.NAVYSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfdc619bd/images/large/PG.949612424S.NAVYSI.BZ.jpg', title: 'Checked Silk Tie, Navy' } ], @@ -5914,16 +5843,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Yellow, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad4fc053/images/large/PG.949612424S.YELLOSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad4fc053/images/large/PG.949612424S.YELLOSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwad4fc053/images/large/PG.949612424S.YELLOSI.PZ.jpg', title: 'Checked Silk Tie, Yellow' }, { alt: 'Checked Silk Tie, Yellow, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd21598ea/images/large/PG.949612424S.YELLOSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd21598ea/images/large/PG.949612424S.YELLOSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd21598ea/images/large/PG.949612424S.YELLOSI.BZ.jpg', title: 'Checked Silk Tie, Yellow' } ], @@ -5945,16 +5872,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, ' }, { alt: 'Checked Silk Tie, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, ' } ], @@ -5966,16 +5891,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Cobalt, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd5ee689c/images/medium/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, Cobalt' }, { alt: 'Checked Silk Tie, Cobalt, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw8bae4d60/images/medium/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, Cobalt' } ], @@ -5997,16 +5920,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Navy, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw244298b8/images/medium/PG.949612424S.NAVYSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw244298b8/images/medium/PG.949612424S.NAVYSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw244298b8/images/medium/PG.949612424S.NAVYSI.PZ.jpg', title: 'Checked Silk Tie, Navy' }, { alt: 'Checked Silk Tie, Navy, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb389c00a/images/medium/PG.949612424S.NAVYSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb389c00a/images/medium/PG.949612424S.NAVYSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb389c00a/images/medium/PG.949612424S.NAVYSI.BZ.jpg', title: 'Checked Silk Tie, Navy' } ], @@ -6028,16 +5949,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Yellow, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdce01d71/images/medium/PG.949612424S.YELLOSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdce01d71/images/medium/PG.949612424S.YELLOSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdce01d71/images/medium/PG.949612424S.YELLOSI.PZ.jpg', title: 'Checked Silk Tie, Yellow' }, { alt: 'Checked Silk Tie, Yellow, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a563bde/images/medium/PG.949612424S.YELLOSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a563bde/images/medium/PG.949612424S.YELLOSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a563bde/images/medium/PG.949612424S.YELLOSI.BZ.jpg', title: 'Checked Silk Tie, Yellow' } ], @@ -6059,16 +5978,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, ' }, { alt: 'Checked Silk Tie, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, ' } ], @@ -6080,16 +5997,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Cobalt, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4fad510c/images/small/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, Cobalt' }, { alt: 'Checked Silk Tie, Cobalt, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9a08e955/images/small/PG.949612424S.COBATSI.BZ.jpg', title: 'Checked Silk Tie, Cobalt' } ], @@ -6111,16 +6026,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Navy, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa94e3279/images/small/PG.949612424S.NAVYSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa94e3279/images/small/PG.949612424S.NAVYSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa94e3279/images/small/PG.949612424S.NAVYSI.PZ.jpg', title: 'Checked Silk Tie, Navy' }, { alt: 'Checked Silk Tie, Navy, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw73571fe7/images/small/PG.949612424S.NAVYSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw73571fe7/images/small/PG.949612424S.NAVYSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw73571fe7/images/small/PG.949612424S.NAVYSI.BZ.jpg', title: 'Checked Silk Tie, Navy' } ], @@ -6142,16 +6055,14 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Yellow, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa9639168/images/small/PG.949612424S.YELLOSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa9639168/images/small/PG.949612424S.YELLOSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa9639168/images/small/PG.949612424S.YELLOSI.PZ.jpg', title: 'Checked Silk Tie, Yellow' }, { alt: 'Checked Silk Tie, Yellow, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbfa3e4f6/images/small/PG.949612424S.YELLOSI.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbfa3e4f6/images/small/PG.949612424S.YELLOSI.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbfa3e4f6/images/small/PG.949612424S.YELLOSI.BZ.jpg', title: 'Checked Silk Tie, Yellow' } ], @@ -6173,8 +6084,7 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Cobalt, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9d87f5f6/images/swatch/PG.949612424S.COBATSI.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9d87f5f6/images/swatch/PG.949612424S.COBATSI.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9d87f5f6/images/swatch/PG.949612424S.COBATSI.CP.jpg', title: 'Checked Silk Tie, Cobalt' } ], @@ -6196,8 +6106,7 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Navy, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa0a33bb5/images/swatch/PG.949612424S.NAVYSI.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa0a33bb5/images/swatch/PG.949612424S.NAVYSI.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa0a33bb5/images/swatch/PG.949612424S.NAVYSI.CP.jpg', title: 'Checked Silk Tie, Navy' } ], @@ -6219,8 +6128,7 @@ export const mockMasterProduct = { alt: 'Checked Silk Tie, Yellow, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw529c07c2/images/swatch/PG.949612424S.YELLOSI.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw529c07c2/images/swatch/PG.949612424S.YELLOSI.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw529c07c2/images/swatch/PG.949612424S.YELLOSI.CP.jpg', title: 'Checked Silk Tie, Yellow' } ], diff --git a/packages/template-retail-react-app/app/commerce-api/mocks/basket-with-suit.js b/packages/template-retail-react-app/app/commerce-api/mocks/basket-with-suit.js index 2f6566e398..14180fb6da 100644 --- a/packages/template-retail-react-app/app/commerce-api/mocks/basket-with-suit.js +++ b/packages/template-retail-react-app/app/commerce-api/mocks/basket-with-suit.js @@ -46,8 +46,7 @@ export default { merchandize_total_tax: 30.0, notes: { _type: 'simple_link', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/9303c9065ccc5fa2c7fca1d4b4/notes' + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/baskets/9303c9065ccc5fa2c7fca1d4b4/notes' }, order_total: 629.98, product_items: [ @@ -95,8 +94,7 @@ export default { { _type: 'shipping_promotion', callout_msg: 'Free Shipping Amount Above 150', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/promotions/3184d71eea54c9d27e88dc41ca', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArch/dw/shop/v21_3/promotions/3184d71eea54c9d27e88dc41ca', promotion_id: 'FreeShippingAmountAbove150', promotion_name: 'Free Shipping Amount Above 150' } diff --git a/packages/template-retail-react-app/app/commerce-api/mocks/einstein-mock-data.js b/packages/template-retail-react-app/app/commerce-api/mocks/einstein-mock-data.js index 839bf9fd2a..159e755d86 100644 --- a/packages/template-retail-react-app/app/commerce-api/mocks/einstein-mock-data.js +++ b/packages/template-retail-react-app/app/commerce-api/mocks/einstein-mock-data.js @@ -50,8 +50,7 @@ export const mockGetZoneRecommendationsResponse = { export const mockCategory = { id: 'mens-accessories-ties', - image: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-non-en/default/dwd2ff3ec8/images/slot/sub_banners/cat-banner-mens-ties.jpg', + image: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-storefront-catalog-m-non-en/default/dwd2ff3ec8/images/slot/sub_banners/cat-banner-mens-ties.jpg', name: 'Ties', pageDescription: "Shop Mens's Ties for all occasions including business or casual at Commerce Cloud", @@ -85,8 +84,7 @@ export const mockSearchResults = { alt: 'Striped Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6e365a5e/images/large/PG.949114314S.REDSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6e365a5e/images/large/PG.949114314S.REDSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6e365a5e/images/large/PG.949114314S.REDSI.PZ.jpg', title: 'Striped Silk Tie, ' }, orderable: true, @@ -133,8 +131,7 @@ export const mockSearchResults = { alt: 'Checked Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwe64d25bd/images/large/PG.949612424S.COBATSI.PZ.jpg', title: 'Checked Silk Tie, ' }, orderable: true, @@ -189,8 +186,7 @@ export const mockSearchResults = { alt: 'Solid Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c618527/images/large/PG.949432114S.NAVYSI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c618527/images/large/PG.949432114S.NAVYSI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1c618527/images/large/PG.949432114S.NAVYSI.PZ.jpg', title: 'Solid Silk Tie, ' }, orderable: true, @@ -245,8 +241,7 @@ export const mockSearchResults = { alt: 'Striped Silk Tie, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4982cf11/images/large/PG.949034314S.TAUPESI.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4982cf11/images/large/PG.949034314S.TAUPESI.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4982cf11/images/large/PG.949034314S.TAUPESI.PZ.jpg', title: 'Striped Silk Tie, ' }, orderable: true, @@ -552,40 +547,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, ' } ], @@ -597,40 +587,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, Gray, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfb7ce066/images/large/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwee33fb7b/images/large/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4a2fbc7b/images/large/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw308eba53/images/large/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwecea3cdd/images/large/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' } ], @@ -643,40 +628,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, ' } ], @@ -688,40 +668,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, Gray, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9bbe03cd/images/medium/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd3a75400/images/medium/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed372d31/images/medium/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw32d43910/images/medium/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6aa00910/images/medium/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' } ], @@ -734,40 +709,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, ' }, { alt: 'Straight Fit Shorts With Button Closure, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, ' } ], @@ -779,40 +749,35 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, Gray, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed10427e/images/small/B0574220_CP1_0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw999b0356/images/small/B0574220_CP1_B0.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3805c600/images/small/B0574220_CP1_L1.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1a024d50/images/small/B0574220_CP1_L2.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' }, { alt: 'Straight Fit Shorts With Button Closure, Gray, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcafa0be3/images/small/B0574220_CP1_L3.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' } ], @@ -825,8 +790,7 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, , swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', title: 'Straight Fit Shorts With Button Closure, ' } ], @@ -838,8 +802,7 @@ export const mockProduct = { alt: 'Straight Fit Shorts With Button Closure, Gray, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa2168062/images/swatch/B0574220_CP1_sw.jpg', title: 'Straight Fit Shorts With Button Closure, Gray' } ], diff --git a/packages/template-retail-react-app/app/commerce-api/mocks/variant-750518699578M.js b/packages/template-retail-react-app/app/commerce-api/mocks/variant-750518699578M.js index b9b32e722c..0168a3639c 100644 --- a/packages/template-retail-react-app/app/commerce-api/mocks/variant-750518699578M.js +++ b/packages/template-retail-react-app/app/commerce-api/mocks/variant-750518699578M.js @@ -14,16 +14,14 @@ export default { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw68c99706/images/large/PG.52001RUBN4Q.BLACKFB.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw68c99706/images/large/PG.52001RUBN4Q.BLACKFB.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw68c99706/images/large/PG.52001RUBN4Q.BLACKFB.PZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' }, { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw45d30402/images/large/PG.52001RUBN4Q.BLACKFB.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw45d30402/images/large/PG.52001RUBN4Q.BLACKFB.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw45d30402/images/large/PG.52001RUBN4Q.BLACKFB.BZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' } ], @@ -45,16 +43,14 @@ export default { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw35242324/images/medium/PG.52001RUBN4Q.BLACKFB.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw35242324/images/medium/PG.52001RUBN4Q.BLACKFB.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw35242324/images/medium/PG.52001RUBN4Q.BLACKFB.PZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' }, { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw116e8397/images/medium/PG.52001RUBN4Q.BLACKFB.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw116e8397/images/medium/PG.52001RUBN4Q.BLACKFB.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw116e8397/images/medium/PG.52001RUBN4Q.BLACKFB.BZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' } ], @@ -76,16 +72,14 @@ export default { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw43b167a3/images/small/PG.52001RUBN4Q.BLACKFB.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw43b167a3/images/small/PG.52001RUBN4Q.BLACKFB.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw43b167a3/images/small/PG.52001RUBN4Q.BLACKFB.PZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' }, { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdcb6bc41/images/small/PG.52001RUBN4Q.BLACKFB.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdcb6bc41/images/small/PG.52001RUBN4Q.BLACKFB.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdcb6bc41/images/small/PG.52001RUBN4Q.BLACKFB.BZ.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' } ], @@ -107,8 +101,7 @@ export default { alt: 'Black Single Pleat Athletic Fit Wool Suit, Black, swatch', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c9bc6f6/images/swatch/PG.52001RUBN4Q.BLACKFB.CP.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c9bc6f6/images/swatch/PG.52001RUBN4Q.BLACKFB.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c9bc6f6/images/swatch/PG.52001RUBN4Q.BLACKFB.CP.jpg', title: 'Black Single Pleat Athletic Fit Wool Suit, Black' } ], diff --git a/packages/template-retail-react-app/app/commerce-api/pkce.js b/packages/template-retail-react-app/app/commerce-api/pkce.js index 6bd20f2788..01a7fc91d0 100644 --- a/packages/template-retail-react-app/app/commerce-api/pkce.js +++ b/packages/template-retail-react-app/app/commerce-api/pkce.js @@ -34,10 +34,7 @@ export const generateCodeChallenge = async (codeVerifier) => { if (isServer) { await import('crypto').then((module) => { const crypto = module.default - base64Digest = crypto - .createHash('sha256') - .update(codeVerifier) - .digest('base64') + base64Digest = crypto.createHash('sha256').update(codeVerifier).digest('base64') }) } else { const encoder = new TextEncoder() @@ -47,8 +44,5 @@ export const generateCodeChallenge = async (codeVerifier) => { base64Digest = base64encode(digest) } - return base64Digest - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=/g, '') + return base64Digest.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') } diff --git a/packages/template-retail-react-app/app/commerce-api/utils.js b/packages/template-retail-react-app/app/commerce-api/utils.js index f653b51a8b..f449b6be14 100644 --- a/packages/template-retail-react-app/app/commerce-api/utils.js +++ b/packages/template-retail-react-app/app/commerce-api/utils.js @@ -52,10 +52,7 @@ const toCamel = (str) => { return str } return str.replace(/([-_][a-z])/gi, ($1) => { - return $1 - .toUpperCase() - .replace('-', '') - .replace('_', '') + return $1.toUpperCase().replace('-', '').replace('_', '') }) } @@ -177,49 +174,44 @@ export const checkRequiredParameters = (listOfPassedParameters, listOfRequiredPa } // This function is used to interact with the OCAPI API -export const createOcapiFetch = (commerceAPIConfig) => async ( - endpoint, - method, - args, - methodName, - body -) => { - const proxy = `/mobify/proxy/ocapi` - - // The api config will only have `ocapiHost` during testing to workaround localhost proxy - const host = commerceAPIConfig.ocapiHost - ? `https://${commerceAPIConfig.ocapiHost}` - : `${getAppOrigin()}${proxy}` - - const siteId = commerceAPIConfig.parameters.siteId - const headers = { - ...args[0].headers, - 'Content-Type': 'application/json', - 'x-dw-client-id': commerceAPIConfig.parameters.clientId - } +export const createOcapiFetch = + (commerceAPIConfig) => async (endpoint, method, args, methodName, body) => { + const proxy = `/mobify/proxy/ocapi` + + // The api config will only have `ocapiHost` during testing to workaround localhost proxy + const host = commerceAPIConfig.ocapiHost + ? `https://${commerceAPIConfig.ocapiHost}` + : `${getAppOrigin()}${proxy}` + + const siteId = commerceAPIConfig.parameters.siteId + const headers = { + ...args[0].headers, + 'Content-Type': 'application/json', + 'x-dw-client-id': commerceAPIConfig.parameters.clientId + } - let response - response = await fetch(`${host}/s/${siteId}/dw/shop/v21_3/${endpoint}`, { - method: method, - headers: headers, - ...(body && { - body: JSON.stringify(body) + let response + response = await fetch(`${host}/s/${siteId}/dw/shop/v21_3/${endpoint}`, { + method: method, + headers: headers, + ...(body && { + body: JSON.stringify(body) + }) }) - }) - const httpStatus = response.status + const httpStatus = response.status - if (!args[1] && response.json) { - response = await response.json() - } + if (!args[1] && response.json) { + response = await response.json() + } - const convertedResponse = keysToCamel(response) - if (convertedResponse.fault) { - const error = convertOcapiFaultToCapiError(convertedResponse.fault) - throw new HTTPError(httpStatus, error.detail) - } else { - return convertedResponse + const convertedResponse = keysToCamel(response) + if (convertedResponse.fault) { + const error = convertOcapiFaultToCapiError(convertedResponse.fault) + throw new HTTPError(httpStatus, error.detail) + } else { + return convertedResponse + } } -} // This function derrives the SF Tenant Id from the SF OrgId export const getTenantId = (orgId) => { diff --git a/packages/template-retail-react-app/app/commerce-api/utils.test.js b/packages/template-retail-react-app/app/commerce-api/utils.test.js index 503f42f0ba..d8eb4e0aff 100644 --- a/packages/template-retail-react-app/app/commerce-api/utils.test.js +++ b/packages/template-retail-react-app/app/commerce-api/utils.test.js @@ -19,6 +19,13 @@ const createJwt = (secondsToExp) => { return token.compact() } +jest.mock('./utils', () => { + const originalModule = jest.requireActual('./utils') + return { + ...originalModule + } +}) + describe('isTokenValid', () => { test('returns false when no token given', () => { expect(isTokenValid()).toBe(false) diff --git a/packages/template-retail-react-app/app/components/_app/index.jsx b/packages/template-retail-react-app/app/components/_app/index.jsx index 74f1ee386c..3052b2a716 100644 --- a/packages/template-retail-react-app/app/components/_app/index.jsx +++ b/packages/template-retail-react-app/app/components/_app/index.jsx @@ -10,7 +10,6 @@ import PropTypes from 'prop-types' import {useHistory, useLocation} from 'react-router-dom' import {getAssetUrl} from 'pwa-kit-react-sdk/ssr/universal/utils' import {getAppOrigin} from 'pwa-kit-react-sdk/utils/url' -import {useCustomerType} from 'commerce-sdk-react-preview' // Chakra import {Box, useDisclosure, useStyleConfig} from '@chakra-ui/react' @@ -44,21 +43,24 @@ import {IntlProvider} from 'react-intl' // Others import {watchOnlineStatus, flatten} from '../../utils/utils' import {getTargetLocale, fetchTranslations} from '../../utils/locale' -import {DEFAULT_SITE_TITLE, HOME_HREF, THEME_COLOR} from '../../constants' +import { + DEFAULT_SITE_TITLE, + HOME_HREF, + THEME_COLOR, + CAT_MENU_DEFAULT_NAV_DEPTH, + CAT_MENU_DEFAULT_ROOT_CATEGORY, + DEFAULT_LOCALE +} from '../../constants' import Seo from '../seo' import {resolveSiteFromUrl} from '../../utils/site-utils' import useMultiSite from '../../hooks/use-multi-site' -import {useCategory} from 'commerce-sdk-react-preview' - -const DEFAULT_NAV_DEPTH = 3 -const DEFAULT_ROOT_CATEGORY = 'root' -const DEFAULT_LOCALE = 'en-US' +import {useCategory, useCustomerType} from 'commerce-sdk-react-preview' const App = (props) => { const {children, targetLocale = DEFAULT_LOCALE, messages = {}} = props const {data: allCategories} = useCategory( - {id: DEFAULT_ROOT_CATEGORY, levels: DEFAULT_NAV_DEPTH}, + {id: CAT_MENU_DEFAULT_ROOT_CATEGORY, levels: CAT_MENU_DEFAULT_NAV_DEPTH}, { select: (categories) => { // Note: What is the best to handle special case like this?? Should commerce sdk handles this? @@ -182,7 +184,7 @@ Learn more with our localization guide. https://sfdc.co/localization-guide // - "compile-translations:pseudo" defaultLocale={DEFAULT_LOCALE} > - + @@ -232,14 +234,18 @@ Learn more with our localization guide. https://sfdc.co/localization-guide isOpen={isOpen} onClose={onClose} onLogoClick={onLogoClick} - root={allCategories?.[DEFAULT_ROOT_CATEGORY]} + root={ + allCategories?.[CAT_MENU_DEFAULT_ROOT_CATEGORY] + } locale={locale} /> diff --git a/packages/template-retail-react-app/app/components/drawer-menu/index.jsx b/packages/template-retail-react-app/app/components/drawer-menu/index.jsx index 9cb705e34c..d6d2a79fd9 100644 --- a/packages/template-retail-react-app/app/components/drawer-menu/index.jsx +++ b/packages/template-retail-react-app/app/components/drawer-menu/index.jsx @@ -13,7 +13,7 @@ import {useIntl} from 'react-intl' import LocaleSelector from '../locale-selector' import NestedAccordion from '../nested-accordion' import SocialIcons from '../social-icons' - +import {useCategories} from '../../hooks/use-categories' // Components import { Box, @@ -57,6 +57,7 @@ import LoadingSpinner from '../loading-spinner' import useNavigation from '../../hooks/use-navigation' import useMultiSite from '../../hooks/use-multi-site' +import {useEffect} from 'react' // The FONT_SIZES and FONT_WEIGHTS constants are used to control the styling for // the accordion buttons as their current depth. In the below definition we assign @@ -81,7 +82,8 @@ const STORE_LOCATOR_HREF = '/store-locator' * main usage is to navigate from one category to the next, but also homes links to * support, log in and out actions, as support links. */ -const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { +const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop}) => { + const {root, itemsKey} = useCategories() const intl = useIntl() const {isRegistered} = useCustomerType() const navigate = useNavigation() @@ -91,6 +93,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { const {site, buildUrl} = useMultiSite() const {l10n} = site const [showLoading, setShowLoading] = useState(false) + const [ariaBusy, setAriaBusy] = useState('true') const logout = useShopperLoginHelper(ShopperLoginHelpers.Logout) const onSignoutClick = async () => { setShowLoading(true) @@ -102,6 +105,10 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { const supportedLocaleIds = l10n?.supportedLocales.map((locale) => locale.id) const showLocaleSelector = supportedLocaleIds?.length > 1 + useEffect(() => { + setAriaBusy('false') + }, []) + return ( @@ -119,49 +126,56 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { {/* Main Content */} - {showLoading && } +
+ {showLoading && } - {/* Category Navigation */} - {root ? ( - - - depth > 0 ? ( - [ - - - {intl.formatMessage({ - id: 'drawer_menu.link.shop_all', - defaultMessage: 'Shop All' - })} - - - ] - ) : ( - <> - ) - } - urlBuilder={categoryUrlBuilder} - /> - - ) : ( -
- -
- )} + {/* Category Navigation */} + {root?.[itemsKey] ? ( + + + depth > 0 ? ( + [ + + + {intl.formatMessage({ + id: 'drawer_menu.link.shop_all', + defaultMessage: 'Shop All' + })} + + + ] + ) : ( + <> + ) + } + urlBuilder={categoryUrlBuilder} + /> + + ) : ( +
+ +
+ )} +
@@ -206,8 +220,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { id: 'profile', path: '', name: intl.formatMessage({ - id: - 'drawer_menu.button.account_details', + id: 'drawer_menu.button.account_details', defaultMessage: 'Account Details' }) }, @@ -215,8 +228,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { id: 'orders', path: '/orders', name: intl.formatMessage({ - id: - 'drawer_menu.button.order_history', + id: 'drawer_menu.button.order_history', defaultMessage: 'Order History' }) }, @@ -232,8 +244,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { id: 'payments', path: '/payments', name: intl.formatMessage({ - id: - 'drawer_menu.button.payment_methods', + id: 'drawer_menu.button.payment_methods', defaultMessage: 'Payment Methods' }) } @@ -303,16 +314,14 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { { id: 'contactus', name: intl.formatMessage({ - id: - 'drawer_menu.link.customer_support.contact_us', + id: 'drawer_menu.link.customer_support.contact_us', defaultMessage: 'Contact Us' }) }, { id: 'shippingandreturns', name: intl.formatMessage({ - id: - 'drawer_menu.link.customer_support.shipping_and_returns', + id: 'drawer_menu.link.customer_support.shipping_and_returns', defaultMessage: 'Shipping & Returns' }) } @@ -387,10 +396,6 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => { DrawerMenu.displayName = 'DrawerMenu' DrawerMenu.propTypes = { - /** - * The root category in your commerce cloud back-end. - */ - root: PropTypes.object, /** * The opened state of the drawer. */ diff --git a/packages/template-retail-react-app/app/components/drawer-menu/index.test.js b/packages/template-retail-react-app/app/components/drawer-menu/index.test.js index 881355d255..10881f924e 100644 --- a/packages/template-retail-react-app/app/components/drawer-menu/index.test.js +++ b/packages/template-retail-react-app/app/components/drawer-menu/index.test.js @@ -8,47 +8,25 @@ import React from 'react' import DrawerMenu from './index' import {renderWithProviders} from '../../utils/test-utils' -const mockRoot = { - id: 't1', - name: 'Test One', - categories: [ - { - id: 't1-1', - name: 'Test One One' - }, - { - id: 't1-2', - name: 'Test One Two', - categories: [ - { - id: 't1-2-1', - name: 'Test One Two One' - }, - { - id: 't1-2-2', - name: 'Test One Two Two' - } - ] - } - ] -} +describe('DrawerMenu', () => { + test('Renders DrawerMenu without errors', async () => { + renderWithProviders() -test('Renders DrawerMenu with root', () => { - renderWithProviders() + const drawer = document.querySelector('.chakra-portal') + const accordion = document.querySelector('.chakra-accordion') + const socialIcons = document.querySelector('.sf-social-icons') - const drawer = document.querySelector('.chakra-portal') - const accordion = document.querySelector('.chakra-accordion') - const socialIcons = document.querySelector('.sf-social-icons') + expect(drawer).toBeInTheDocument() + expect(accordion).toBeInTheDocument() + expect(socialIcons).toBeInTheDocument() + }) + test('Renders DrawerMenu Spinner without root', async () => { + renderWithProviders(, { + wrapperProps: {initialCategories: {}} + }) - expect(drawer).toBeInTheDocument() - expect(accordion).toBeInTheDocument() - expect(socialIcons).toBeInTheDocument() -}) - -test('Renders DrawerMenu Spinner without root', () => { - renderWithProviders() - - const spinner = document.querySelector('.chakra-spinner') + const spinner = document.querySelector('.chakra-spinner') - expect(spinner).toBeInTheDocument() + expect(spinner).toBeInTheDocument() + }) }) diff --git a/packages/template-retail-react-app/app/components/header/index.test.js b/packages/template-retail-react-app/app/components/header/index.test.js index 48156bf25a..11b4847d81 100644 --- a/packages/template-retail-react-app/app/components/header/index.test.js +++ b/packages/template-retail-react-app/app/components/header/index.test.js @@ -4,18 +4,32 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import React, {useEffect} from 'react' +import React from 'react' import PropTypes from 'prop-types' - -import {fireEvent, screen, waitFor} from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import {fireEvent, screen, waitFor, act} from '@testing-library/react' import Header from './index' import {renderWithProviders, createPathWithDefaults} from '../../utils/test-utils' -import useCustomer from '../../commerce-api/hooks/useCustomer' import {rest} from 'msw' -import {mockedCustomerProductLists} from '../../commerce-api/mock-data' import {Route, Switch} from 'react-router-dom' import {createMemoryHistory} from 'history' +import {mockCustomerBaskets, mockedRegisteredCustomer} from '../../commerce-api/mock-data' +import { + ShopperLoginHelpers, + useCustomerType, + useShopperLoginHelper +} from 'commerce-sdk-react-preview' +jest.mock('commerce-sdk-react-preview', () => { + const originModule = jest.requireActual('commerce-sdk-react-preview') + return { + ...originModule, + useCustomerId: jest.fn().mockReturnValue('customer_id'), + useCustomerType: jest + .fn() + .mockReturnValue({isRegistered: false, isGuest: true, customerType: 'guest'}) + } +}) jest.mock('@chakra-ui/react', () => { const originalModule = jest.requireActual('@chakra-ui/react') return { @@ -23,30 +37,27 @@ jest.mock('@chakra-ui/react', () => { useMediaQuery: jest.fn().mockReturnValue([true]) } }) - const MockedComponent = ({history}) => { - const customer = useCustomer() - useEffect(() => { - if (!customer.isRegistered) { - customer.login('customer@test.com', 'password1') - } + const loginRegisteredUser = useShopperLoginHelper(ShopperLoginHelpers.LoginRegisteredUserB2C) + React.useEffect(() => { + loginRegisteredUser.mutate({username: 'customer@test.com', password: 'password1'}) }, []) + const {isRegistered, customerType} = useCustomerType() const onAccountClick = () => { // Link to account page for registered customer, open auth modal otherwise - if (customer.isRegistered) { + if (isRegistered) { history.push(createPathWithDefaults('/account')) } } const onWishlistClick = () => { history.push(createPathWithDefaults('/account/wishlist')) } - return (
-
home page
+
home page {customerType}
@@ -59,76 +70,97 @@ MockedComponent.propTypes = { // Set up and clean up beforeEach(() => { jest.resetModules() - // Since we're testing some navigation logic, we are using a simple Router - // around our component. We need to initialize the default route/path here. - window.history.pushState({}, 'Account', createPathWithDefaults('/account')) + global.server.use( + rest.get('*/customers/:customerId/baskets', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockCustomerBaskets)) + }) + ) }) afterEach(() => { localStorage.clear() }) - -test('renders Header', () => { - renderWithProviders(
) - const menu = document.querySelector('button[aria-label="Menu"]') - const logo = document.querySelector('button[aria-label="Logo"]') - const account = document.querySelector('svg[aria-label="My account"]') - const cart = document.querySelector('button[aria-label="My cart"]') - const wishlist = document.querySelector('button[aria-label="Wishlist"]') - const searchInput = document.querySelector('input[type="search"]') - expect(menu).toBeInTheDocument() - expect(logo).toBeInTheDocument() - expect(account).toBeInTheDocument() - expect(cart).toBeInTheDocument() - expect(wishlist).toBeInTheDocument() - expect(searchInput).toBeInTheDocument() +test('renders Header', async () => { + await act(async () => { + renderWithProviders(
) + }) + await waitFor(() => { + const menu = document.querySelector('button[aria-label="Menu"]') + const logo = document.querySelector('button[aria-label="Logo"]') + const account = document.querySelector('svg[aria-label="My account"]') + const cart = document.querySelector('button[aria-label="My cart"]') + const wishlist = document.querySelector('button[aria-label="Wishlist"]') + const searchInput = document.querySelector('input[type="search"]') + expect(menu).toBeInTheDocument() + expect(logo).toBeInTheDocument() + expect(account).toBeInTheDocument() + expect(cart).toBeInTheDocument() + expect(wishlist).toBeInTheDocument() + expect(searchInput).toBeInTheDocument() + }) }) -test('renders Header with event handlers', () => { +test('renders Header with event handlers', async () => { const onMenuClick = jest.fn() const onLogoClick = jest.fn() const onMyAccountClick = jest.fn() const onMyCartClick = jest.fn() - renderWithProviders( -
- ) - const menu = document.querySelector('button[aria-label="Menu"]') - const logo = document.querySelector('button[aria-label="Logo"]') - const account = document.querySelector('svg[aria-label="My account"]') - const cart = document.querySelector('button[aria-label="My cart"]') - - fireEvent.click(menu) - expect(onMenuClick).toHaveBeenCalledTimes(1) - fireEvent.click(logo) - expect(onLogoClick).toHaveBeenCalledTimes(1) - fireEvent.click(account) - expect(onMyAccountClick).toHaveBeenCalledTimes(1) - fireEvent.click(cart) - expect(onMyCartClick).toHaveBeenCalledTimes(1) + await act(async () => { + renderWithProviders( +
+ ) + }) + await waitFor(() => { + const menu = document.querySelector('button[aria-label="Menu"]') + const logo = document.querySelector('button[aria-label="Logo"]') + const account = document.querySelector('svg[aria-label="My account"]') + const cart = document.querySelector('button[aria-label="My cart"]') + expect(menu).toBeInTheDocument() + fireEvent.click(menu) + expect(onMenuClick).toHaveBeenCalledTimes(1) + fireEvent.click(logo) + expect(onLogoClick).toHaveBeenCalledTimes(1) + fireEvent.click(account) + expect(onMyAccountClick).toHaveBeenCalledTimes(1) + fireEvent.click(cart) + expect(onMyCartClick).toHaveBeenCalledTimes(1) + }) }) /** * The badge component on the cart that shows the number of items in the cart * should only be displayed when there is a valid cart loaded. */ -const testBaskets = [null, undefined, {basketId: null}, {basketId: undefined}] - -test.each(testBaskets)('does not render cart badge when basket not loaded', (initialBasket) => { - renderWithProviders(
, {wrapperProps: {initialBasket}}) +const testBaskets = [null, undefined, {total: 0}] - // Look for badge. - const badge = document.querySelector('button[aria-label="My cart"] .chakra-badge') +test.each(testBaskets)( + `does not render cart badge when basket value is not defined`, + async (initialBasket) => { + global.server.use( + rest.get('*/customers/:customerId/baskets', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(initialBasket)) + }) + ) + await act(async () => { + renderWithProviders(
) + }) - expect(badge).toBeNull() -}) + await waitFor(() => { + // Look for badge. + const badge = document.querySelector('button[aria-label="My cart"] .chakra-badge') + expect(badge).not.toBeInTheDocument() + }) + } +) test('renders cart badge when basket is loaded', async () => { - const initialBasket = {basketId: 'valid_id'} - renderWithProviders(
, {wrapperProps: {initialBasket}}) + await act(async () => { + renderWithProviders(
) + }) await waitFor(() => { // Look for badge. @@ -138,10 +170,18 @@ test('renders cart badge when basket is loaded', async () => { }) test('route to account page when an authenticated users click on account icon', async () => { + global.server.use( + rest.post('*/customers/action/login', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer)) + }) + ) + useCustomerType.mockReturnValue({isRegistered: true, customerType: 'registered'}) const history = createMemoryHistory() // mock push function history.push = jest.fn() - renderWithProviders() + await act(async () => { + renderWithProviders() + }) await waitFor(() => { // Look for account icon @@ -160,41 +200,62 @@ test('route to account page when an authenticated users click on account icon', }) }) -test('route to wishlist page when an authenticated users click on wishlist icon', async () => { +//TODO: fix this test after wishlist is integrated with hook, +// currently it is still using the old login method while Header is using a new login +// test('route to wishlist page when an authenticated users click on wishlist icon', async () => { +// const history = createMemoryHistory() +// // mock push function +// history.push = jest.fn() +// useCustomerType.mockReturnValue({isRegistered: true, customerType: 'registered'}) +// +// await act(async () => { +// renderWithProviders() +// }) +// +// await waitFor(() => { +// // Look for account icon +// const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]') +// expect(accountTrigger).toBeInTheDocument() +// }) +// const wishlistIcon = screen.getByRole('button', {name: /wishlist/i}) +// console.log('wishlistIcon', wishlistIcon) +// userEvent.click(wishlistIcon) +// await waitFor(() => { +// expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account/wishlist')) +// }) +// }) + +test('shows dropdown menu when an authenticated users hover on the account icon', async () => { + global.server.use( + rest.post('*/customers/action/login', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer)) + }) + ) const history = createMemoryHistory() // mock push function history.push = jest.fn() - renderWithProviders() + useCustomerType.mockReturnValue({isRegistered: true, customerType: 'registered'}) + await act(async () => { + renderWithProviders() + }) await waitFor(() => { // Look for account icon const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]') expect(accountTrigger).toBeInTheDocument() }) - const wishlistIcon = document.querySelector('button[aria-label="Wishlist"]') - fireEvent.click(wishlistIcon) + const accountIcon = document.querySelector('svg[aria-label="My account"]') + fireEvent.click(accountIcon) await waitFor(() => { - expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account/wishlist')) + expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account')) }) -}) - -test('shows dropdown menu when an authenticated users hover on the account icon', async () => { - global.server.use( - // mock fetch product lists - rest.get('*/customers/:customerId/product-lists', (req, res, ctx) => { - return res(ctx.json(mockedCustomerProductLists)) - }) - ) - renderWithProviders() - // Look for account icon - const account = document.querySelector('svg[aria-label="My account"]') - - fireEvent.mouseOver(account) + userEvent.hover(accountIcon) await waitFor(() => { - // Look for account icon - const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]') - expect(accountTrigger).toBeInTheDocument() - expect(screen.getByText('My Account')).toBeInTheDocument() + expect(screen.getByText(/account details/i)).toBeInTheDocument() + expect(screen.getByText(/payment methods/i)).toBeInTheDocument() + expect(screen.getByText(/addresses/i)).toBeInTheDocument() + expect(screen.getByText(/wishlist/i)).toBeInTheDocument() + expect(screen.getByText(/order history/i)).toBeInTheDocument() }) }) diff --git a/packages/template-retail-react-app/app/components/image-gallery/index.test.js b/packages/template-retail-react-app/app/components/image-gallery/index.test.js index 3a193bc09b..26f09a991b 100644 --- a/packages/template-retail-react-app/app/components/image-gallery/index.test.js +++ b/packages/template-retail-react-app/app/components/image-gallery/index.test.js @@ -83,16 +83,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' }, { alt: 'Ruffle Front V-Neck Cardigan, , large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' } ], @@ -104,16 +102,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Black, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' }, { alt: 'Ruffle Front V-Neck Cardigan, Black, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' } ], @@ -135,16 +131,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa1d99f48/images/large/PG.10216885.JJ8UTXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa1d99f48/images/large/PG.10216885.JJ8UTXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa1d99f48/images/large/PG.10216885.JJ8UTXX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' }, { alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2aee1854/images/large/PG.10216885.JJ8UTXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2aee1854/images/large/PG.10216885.JJ8UTXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2aee1854/images/large/PG.10216885.JJ8UTXX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' } ], @@ -166,16 +160,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw01a849a1/images/large/PG.10216885.JJ908XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw01a849a1/images/large/PG.10216885.JJ908XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw01a849a1/images/large/PG.10216885.JJ908XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' }, { alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw54a4f8aa/images/large/PG.10216885.JJ908XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw54a4f8aa/images/large/PG.10216885.JJ908XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw54a4f8aa/images/large/PG.10216885.JJ908XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' } ], @@ -197,16 +189,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, White, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc71d06b7/images/large/PG.10216885.JJI15XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc71d06b7/images/large/PG.10216885.JJI15XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc71d06b7/images/large/PG.10216885.JJI15XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' }, { alt: 'Ruffle Front V-Neck Cardigan, White, large', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw52d9334e/images/large/PG.10216885.JJI15XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw52d9334e/images/large/PG.10216885.JJI15XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw52d9334e/images/large/PG.10216885.JJI15XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' } ], @@ -228,16 +218,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' }, { alt: 'Ruffle Front V-Neck Cardigan, , medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' } ], @@ -249,16 +237,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Black, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' }, { alt: 'Ruffle Front V-Neck Cardigan, Black, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' } ], @@ -280,16 +266,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwae3c27b4/images/medium/PG.10216885.JJ8UTXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwae3c27b4/images/medium/PG.10216885.JJ8UTXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwae3c27b4/images/medium/PG.10216885.JJ8UTXX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' }, { alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbbada6e/images/medium/PG.10216885.JJ8UTXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbbada6e/images/medium/PG.10216885.JJ8UTXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcbbada6e/images/medium/PG.10216885.JJ8UTXX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' } ], @@ -311,16 +295,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw636662ae/images/medium/PG.10216885.JJ908XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw636662ae/images/medium/PG.10216885.JJ908XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw636662ae/images/medium/PG.10216885.JJ908XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' }, { alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw77947f93/images/medium/PG.10216885.JJ908XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw77947f93/images/medium/PG.10216885.JJ908XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw77947f93/images/medium/PG.10216885.JJ908XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' } ], @@ -342,16 +324,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, White, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa32e1f75/images/medium/PG.10216885.JJI15XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa32e1f75/images/medium/PG.10216885.JJI15XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa32e1f75/images/medium/PG.10216885.JJI15XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' }, { alt: 'Ruffle Front V-Neck Cardigan, White, medium', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw10b25b53/images/medium/PG.10216885.JJI15XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw10b25b53/images/medium/PG.10216885.JJI15XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw10b25b53/images/medium/PG.10216885.JJI15XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' } ], @@ -373,16 +353,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' }, { alt: 'Ruffle Front V-Neck Cardigan, , small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, ' } ], @@ -394,16 +372,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Black, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' }, { alt: 'Ruffle Front V-Neck Cardigan, Black, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Black' } ], @@ -425,16 +401,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5d6b00b9/images/small/PG.10216885.JJ8UTXX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5d6b00b9/images/small/PG.10216885.JJ8UTXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5d6b00b9/images/small/PG.10216885.JJ8UTXX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' }, { alt: 'Ruffle Front V-Neck Cardigan, Icy Mint, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfa36098c/images/small/PG.10216885.JJ8UTXX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfa36098c/images/small/PG.10216885.JJ8UTXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfa36098c/images/small/PG.10216885.JJ8UTXX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Icy Mint' } ], @@ -456,16 +430,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw61257164/images/small/PG.10216885.JJ908XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw61257164/images/small/PG.10216885.JJ908XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw61257164/images/small/PG.10216885.JJ908XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' }, { alt: 'Ruffle Front V-Neck Cardigan, Grey Heather, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbafa96c5/images/small/PG.10216885.JJ908XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbafa96c5/images/small/PG.10216885.JJ908XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbafa96c5/images/small/PG.10216885.JJ908XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, Grey Heather' } ], @@ -487,16 +459,14 @@ const data = [ alt: 'Ruffle Front V-Neck Cardigan, White, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbee5712e/images/small/PG.10216885.JJI15XX.PZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbee5712e/images/small/PG.10216885.JJI15XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbee5712e/images/small/PG.10216885.JJI15XX.PZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' }, { alt: 'Ruffle Front V-Neck Cardigan, White, small', disBaseLink: 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d458e21/images/small/PG.10216885.JJI15XX.BZ.jpg', - link: - 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d458e21/images/small/PG.10216885.JJI15XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw6d458e21/images/small/PG.10216885.JJI15XX.BZ.jpg', title: 'Ruffle Front V-Neck Cardigan, White' } ], diff --git a/packages/template-retail-react-app/app/components/list-menu/index.jsx b/packages/template-retail-react-app/app/components/list-menu/index.jsx index ca138dd7fc..a639c9acdf 100644 --- a/packages/template-retail-react-app/app/components/list-menu/index.jsx +++ b/packages/template-retail-react-app/app/components/list-menu/index.jsx @@ -5,10 +5,11 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import React, {Fragment, useRef, forwardRef} from 'react' +import React, {Fragment, useRef, forwardRef, useEffect, useState} from 'react' import PropTypes from 'prop-types' import {useIntl} from 'react-intl' import {Link as RouteLink} from 'react-router-dom' +import {useCategories} from '../../hooks/use-categories' // Project Components import LinksList from '../links-list' @@ -18,6 +19,7 @@ import { Box, Container, SimpleGrid, + Fade, Flex, Stack, Popover, @@ -63,27 +65,26 @@ const ListMenuTrigger = ({item, name, isOpen, onOpen, onClose, hasItems}) => { onMouseOver={onOpen} {...baseStyle.listMenuTriggerLink} {...(hasItems ? {name: name + ' __'} : {name: name})} - {...(!hasItems ? baseStyle.listMenuTriggerLinkWithIcon : {})} {...(isOpen ? baseStyle.listMenuTriggerLinkActive : {})} > {name} - {hasItems && ( - { - keyMap[e.key]?.(e) - }} - {...baseStyle.listMenuTriggerLinkIcon} - > - + { + keyMap[e.key]?.(e) + }} + {...baseStyle.listMenuTriggerLinkIcon} + > + + - - - )} + + + ) } @@ -160,9 +161,9 @@ const ListMenuContent = ({maxColumns, items, itemsKey, onClose, initialFocusRef} ListMenuContent.propTypes = { items: PropTypes.array, maxColumns: PropTypes.number, - itemsKey: PropTypes.string, onClose: PropTypes.func, - initialFocusRef: PropTypes.object + initialFocusRef: PropTypes.object, + itemsKey: PropTypes.string } const ListMenuPopover = ({items, item, name, itemsKey, maxColumns}) => { @@ -217,34 +218,42 @@ ListMenuPopover.propTypes = { * The submenus are open when the user moves the mouse over the trigger and for A11y when * users use the keyboard Tab key to focus over the chevron icon and press Enter. * - * @param root The root category in your commerce cloud back-end. * @param maxColumns The maximum number of columns that we want to use per row inside the ListMenu. */ -const ListMenu = ({root, maxColumns = MAXIMUM_NUMBER_COLUMNS}) => { +const ListMenu = ({maxColumns = MAXIMUM_NUMBER_COLUMNS}) => { + const {root, itemsKey} = useCategories() const theme = useTheme() const {baseStyle} = theme.components.ListMenu + const [ariaBusy, setAriaBusy] = useState('true') - const itemsKey = 'categories' - const items = root ? root[itemsKey] : false + useEffect(() => { + setAriaBusy('false') + }, []) return ( -