From ee6687670e8cdeea9a887c2ee0fa5d7d52f9f253 Mon Sep 17 00:00:00 2001 From: Andy Clifford Date: Thu, 5 Oct 2023 15:09:40 +1300 Subject: [PATCH] Detect if new changes break existing links Added a unit test which determines if any static links have been broken by iterating over the build output and comparing it to a snapshot of existing links. The test will fail if a page is removed and no redirect is added for it. --- .github/workflows/validate.yml | 1 + Dockerfile.integration | 3 + README.md | 8 + docker-compose-dev.yml | 2 + docker-compose.yml | 11 + package.json | 7 +- .../links.integration.test.js.snap | 647 ++++++++++++++++++ src/__tests__/links.integration.test.js | 40 ++ vitest.config.js | 3 +- vitest.integration.config.js | 8 + yarn.lock | 283 +++++++- 11 files changed, 1007 insertions(+), 6 deletions(-) create mode 100644 Dockerfile.integration create mode 100644 src/__tests__/__snapshots__/links.integration.test.js.snap create mode 100644 src/__tests__/links.integration.test.js create mode 100644 vitest.integration.config.js diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1fd397271..f9cf9e28f 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -30,3 +30,4 @@ jobs: - run: yarn test - run: ./build.sh - run: yarn linkinator + - run: yarn integration diff --git a/Dockerfile.integration b/Dockerfile.integration new file mode 100644 index 000000000..7bcdae511 --- /dev/null +++ b/Dockerfile.integration @@ -0,0 +1,3 @@ +FROM node:18-alpine + +RUN apk add rsync diff --git a/README.md b/README.md index 2eaf6c447..70e63d5e0 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,11 @@ To build and preview Centrapay Docs using the static files that are deployed to docker compose run script yarn build docker compose run script yarn preview ``` + +## Integration Tests + +``` +docker compose run script yarn build --mode development +docker compose run ruby-script bundle exec jekyll build +docker compose run integration yarn integration +``` diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 198503036..4d091e7c5 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -5,6 +5,8 @@ services: volumes: [ node-modules:/work/node_modules ] script: volumes: [ node-modules:/work/node_modules ] + integration: + volumes: [ node-modules:/work/node_modules ] legacy: volumes: [ bundle:/usr/local/bundle ] ruby-script: diff --git a/docker-compose.yml b/docker-compose.yml index 5bcf53309..784953d2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,3 +46,14 @@ services: echo " To execute a script, run: docker-compose run ruby-script {command}" + integration: + build: + context: . + dockerfile: Dockerfile.integration + init: true + working_dir: /work + volumes: [ ".:/work" ] + command: | + echo ' + Skipping integration tests. To execute integration tests, run: + docker compose run integration yarn integration' diff --git a/package.json b/package.json index a1b1c9500..9fdca0fea 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "test": "vitest --watch=false" + "test": "vitest --watch=false", + "integration": "rsync -a dist/* _site/ && vitest --watch=false --config vitest.integration.config.js" }, "devDependencies": { "@astrojs/mdx": "^1.0.3", @@ -32,6 +33,7 @@ "linkinator": "^5.0.2", "mdast-util-to-string": "^3.1.1", "mermaid": "^9.3.0", + "path": "^0.12.7", "remark-html": "^15.0.2", "remark-parse": "^10.0.1", "remark-sectionize": "^1.1.1", @@ -40,6 +42,7 @@ "unist-util-find-after": "^4.0.1", "unist-util-visit": "^4.1.2", "vitest": "^0.28.3", - "vue": "^3.2.47" + "vue": "^3.2.47", + "jsdom": "^22.1.0" } } diff --git a/src/__tests__/__snapshots__/links.integration.test.js.snap b/src/__tests__/__snapshots__/links.integration.test.js.snap new file mode 100644 index 000000000..6284342b7 --- /dev/null +++ b/src/__tests__/__snapshots__/links.integration.test.js.snap @@ -0,0 +1,647 @@ +// Vitest Snapshot v1 + +exports[`the build should not break any links 1`] = ` +[ + "", + "/404", + "/accounts", + "/api", + "/api/account-memberships", + "/api/account-memberships#account-member", + "/api/account-memberships#account-memberships", + "/api/account-memberships#add-member-experimental", + "/api/account-memberships#contents", + "/api/account-memberships#list-account-memberships-for-authenticated-subject", + "/api/account-memberships#list-account-memberships-for-specific-user", + "/api/account-memberships#list-members", + "/api/account-memberships#models", + "/api/account-memberships#operations", + "/api/account-memberships#revoke-member", + "/api/account-memberships#top", + "/api/accounts", + "/api/accounts#account", + "/api/accounts#accounts", + "/api/accounts#contents", + "/api/accounts#create-an-account", + "/api/accounts#get-an-account", + "/api/accounts#models", + "/api/accounts#operations", + "/api/accounts#subscription", + "/api/accounts#top", + "/api/accounts#update-an-account", + "/api/accounts#update-subscriptions", + "/api/api-keys", + "/api/api-keys#api-key", + "/api/api-keys#api-keys", + "/api/api-keys#contents", + "/api/api-keys#create-an-api-key", + "/api/api-keys#list-api-keys", + "/api/api-keys#models", + "/api/api-keys#operations", + "/api/api-keys#top", + "/api/api-keys#update-an-api-key", + "/api/asset-categories", + "/api/asset-transfers", + "/api/asset-transfers#asset-transfer-lifecycle", + "/api/asset-transfers#asset-transfers", + "/api/asset-transfers#contents", + "/api/asset-transfers#create-an-asset-transfer", + "/api/asset-transfers#get-an-asset-transfer", + "/api/asset-transfers#list-asset-transfers-experimental", + "/api/asset-transfers#resolve-claimable-assets-experimental", + "/api/asset-transfers#top", + "/api/asset-types", + "/api/asset-types#supported-asset-types", + "/api/asset-types#top", + "/api/assets", + "/api/assets#archive-asset", + "/api/assets#asset", + "/api/assets#assets", + "/api/assets#contents", + "/api/assets#get-asset", + "/api/assets#gift-cards", + "/api/assets#list-assets-for-account", + "/api/assets#listing-asset-transactions-experimental", + "/api/assets#models", + "/api/assets#money", + "/api/assets#operations", + "/api/assets#tokens-experimental", + "/api/assets#top", + "/api/auth", + "/api/auth#account-flags", + "/api/auth#api-keys", + "/api/auth#auth", + "/api/auth#authenticating-api-calls", + "/api/auth#claims", + "/api/auth#contents", + "/api/auth#permissions", + "/api/auth#roles-and-permissions", + "/api/auth#top", + "/api/auth#user-access-tokens", + "/api/bank-account-approvals", + "/api/bank-account-approvals#accept-a-bank-account-approval-experimental", + "/api/bank-account-approvals#bank-account-approval", + "/api/bank-account-approvals#bank-account-approval-activity", + "/api/bank-account-approvals#bank-account-approval-types", + "/api/bank-account-approvals#bank-account-approvals", + "/api/bank-account-approvals#contents", + "/api/bank-account-approvals#decline-a-bank-account-approval-experimental", + "/api/bank-account-approvals#get-bank-account-approval-experimental", + "/api/bank-account-approvals#list-bank-account-approvals-experimental", + "/api/bank-account-approvals#models", + "/api/bank-account-approvals#operations", + "/api/bank-account-approvals#request-bank-account-approval-experimental", + "/api/bank-account-approvals#top", + "/api/bank-account-connection-intents", + "/api/bank-account-connection-intents#authorize-bank-account-connection-intent-experimental", + "/api/bank-account-connection-intents#bank-account-connection-intent", + "/api/bank-account-connection-intents#bank-account-connection-intent-types", + "/api/bank-account-connection-intents#bank-account-connection-intents", + "/api/bank-account-connection-intents#contents", + "/api/bank-account-connection-intents#create-bank-account-connection-intent-experimental", + "/api/bank-account-connection-intents#models", + "/api/bank-account-connection-intents#operations", + "/api/bank-account-connection-intents#top", + "/api/bank-accounts", + "/api/bank-accounts#adding-a-direct-debit-authority-to-a-bank-account", + "/api/bank-accounts#bank-account", + "/api/bank-accounts#bank-account-approval-type-summary-experimental", + "/api/bank-accounts#bank-account-balance-experimental", + "/api/bank-accounts#bank-account-create", + "/api/bank-accounts#bank-account-list", + "/api/bank-accounts#bank-account-type-experimental", + "/api/bank-accounts#bank-accounts", + "/api/bank-accounts#bank-authority-get", + "/api/bank-accounts#contents", + "/api/bank-accounts#creating-a-bank-account", + "/api/bank-accounts#creating-a-bank-authority-deprecated", + "/api/bank-accounts#get-bank-account-balance-experimental", + "/api/bank-accounts#get-information-about-a-bank-account", + "/api/bank-accounts#get-information-about-a-bank-authority-deprecated", + "/api/bank-accounts#list-bank-accounts", + "/api/bank-accounts#list-bank-authorities-deprecated", + "/api/bank-accounts#models", + "/api/bank-accounts#operations", + "/api/bank-accounts#top", + "/api/bank-accounts#verify-a-bank-account", + "/api/bank-accounts#verify-a-bank-authority-deprecated", + "/api/bank-accounts#verify-bank-account", + "/api/batch-types/farmlands", + "/api/batch-types/farmlands-external-asset", + "/api/batch-types/farmlands-external-asset#account", + "/api/batch-types/farmlands-external-asset#card", + "/api/batch-types/farmlands-external-asset#contact", + "/api/batch-types/farmlands-external-asset#contents", + "/api/batch-types/farmlands-external-asset#example", + "/api/batch-types/farmlands-external-asset#example-jsonl-file", + "/api/batch-types/farmlands-external-asset#example-models", + "/api/batch-types/farmlands-external-asset#farmlands-external-asset-batch", + "/api/batch-types/farmlands-external-asset#models", + "/api/batch-types/farmlands-external-asset#top", + "/api/batch-types/verifone-terminal-status", + "/api/batch-types/verifone-terminal-status#contents", + "/api/batch-types/verifone-terminal-status#example-jsonl-file", + "/api/batch-types/verifone-terminal-status#models", + "/api/batch-types/verifone-terminal-status#pretty-printed-example", + "/api/batch-types/verifone-terminal-status#terminal-status", + "/api/batch-types/verifone-terminal-status#top", + "/api/batch-types/verifone-terminal-status#verifone-terminal-status-batch", + "/api/batches", + "/api/batches#batch", + "/api/batches#batch-lifecycle", + "/api/batches#batch-types", + "/api/batches#batches", + "/api/batches#contents", + "/api/batches#create-batch-experimental", + "/api/batches#error", + "/api/batches#get-batch-experimental", + "/api/batches#models", + "/api/batches#operations", + "/api/batches#top", + "/api/businesses", + "/api/businesses#business", + "/api/businesses#businesses", + "/api/businesses#contents", + "/api/businesses#create-a-business-experimental", + "/api/businesses#get-a-business-by-account-id-experimental", + "/api/businesses#get-business-details-from-nz-company-register-experimental", + "/api/businesses#models", + "/api/businesses#onboarding-status-reasons", + "/api/businesses#onboarding-statuses", + "/api/businesses#operations", + "/api/businesses#search-nz-company-register-experimental", + "/api/businesses#set-business-onboarding-status", + "/api/businesses#tax-number", + "/api/businesses#top", + "/api/businesses#update-a-business-experimental", + "/api/data-types", + "/api/data-types#bignumber", + "/api/data-types#crn", + "/api/data-types#data-types", + "/api/data-types#location", + "/api/data-types#monetary", + "/api/data-types#phonenumber", + "/api/data-types#timestamp", + "/api/data-types#top", + "/api/discrete-assets", + "/api/external-assets", + "/api/external-assets#contents", + "/api/external-assets#external-assets", + "/api/external-assets#load-an-external-asset", + "/api/external-assets#top", + "/api/funds-transfers", + "/api/funds-transfers#abort-funds-transfer", + "/api/funds-transfers#contents", + "/api/funds-transfers#creating-a-top-up", + "/api/funds-transfers#creating-a-withdrawal-experimental", + "/api/funds-transfers#funds-transfers", + "/api/funds-transfers#get-a-top-up", + "/api/funds-transfers#get-a-withdrawal-experimental", + "/api/funds-transfers#list-top-ups-for-an-account-experimental", + "/api/funds-transfers#list-top-ups-for-authorized-accounts", + "/api/funds-transfers#list-withdrawals-for-an-account-experimental", + "/api/funds-transfers#top", + "/api/http-status-codes", + "/api/http-status-codes#200-ok", + "/api/http-status-codes#400-malformed-request", + "/api/http-status-codes#401-unauthorized", + "/api/http-status-codes#403-forbidden", + "/api/http-status-codes#404-route-not-found", + "/api/http-status-codes#429-too-many-requests", + "/api/http-status-codes#5xx-server-error", + "/api/http-status-codes#business-rule-violated", + "/api/http-status-codes#contents", + "/api/http-status-codes#http-status-codes", + "/api/http-status-codes#resource-missing-or-permission-denied", + "/api/http-status-codes#top", + "/api/integration-requests", + "/api/integration-requests#activate-integration-request-experimental", + "/api/integration-requests#configure-integration-request-experimental", + "/api/integration-requests#contents", + "/api/integration-requests#create-an-integration-request-experimental", + "/api/integration-requests#delete-integration-request-experimental", + "/api/integration-requests#get-an-integration-request-experimental", + "/api/integration-requests#get-integration-request-configuration-experimental", + "/api/integration-requests#integration-request", + "/api/integration-requests#integration-requests", + "/api/integration-requests#integration-types", + "/api/integration-requests#models", + "/api/integration-requests#operations", + "/api/integration-requests#product", + "/api/integration-requests#search-integration-requests-experimental", + "/api/integration-requests#terminal", + "/api/integration-requests#top", + "/api/integrations", + "/api/integrations#integrations", + "/api/integrations#top", + "/api/introduction", + "/api/introduction#api-introduction", + "/api/introduction#top", + "/api/invitations", + "/api/invitations#accept-an-invitation-experimental", + "/api/invitations#contents", + "/api/invitations#create-an-invitation-experimental", + "/api/invitations#get-an-invitation-by-code-experimental", + "/api/invitations#invitation", + "/api/invitations#invitations", + "/api/invitations#list-invitations-by-accountid-experimental", + "/api/invitations#models", + "/api/invitations#operations", + "/api/invitations#params", + "/api/invitations#revoke-an-invitation-experimental", + "/api/invitations#top", + "/api/legacy-payment-requests", + "/api/legacy-payment-requests#cancelling-a-payment-request", + "/api/legacy-payment-requests#contents", + "/api/legacy-payment-requests#creating-a-payment-request", + "/api/legacy-payment-requests#decoded-webhook-jwt-examples", + "/api/legacy-payment-requests#getting-the-information-about-a-payment-request", + "/api/legacy-payment-requests#legacy-payment-requests", + "/api/legacy-payment-requests#life-cycle-events-that-trigger-webhooks", + "/api/legacy-payment-requests#operations", + "/api/legacy-payment-requests#paying-a-payment-request", + "/api/legacy-payment-requests#payment-request-cancelled", + "/api/legacy-payment-requests#payment-request-cancelled-1", + "/api/legacy-payment-requests#payment-request-expired", + "/api/legacy-payment-requests#refunding-a-transaction", + "/api/legacy-payment-requests#requests-cancel", + "/api/legacy-payment-requests#requests-create", + "/api/legacy-payment-requests#requests-info", + "/api/legacy-payment-requests#requests-pay", + "/api/legacy-payment-requests#requests-void", + "/api/legacy-payment-requests#top", + "/api/legacy-payment-requests#transaction-completed", + "/api/legacy-payment-requests#transaction-completed-successfully", + "/api/legacy-payment-requests#transaction-refunded", + "/api/legacy-payment-requests#transactions-refund", + "/api/legacy-payment-requests#voiding-a-payment-request", + "/api/legacy-payment-requests#webhook-jwt-validation", + "/api/legacy-payment-requests#webhook-payload", + "/api/legacy-payment-requests#webhook-payload-fields", + "/api/legacy-payment-requests#webhooks", + "/api/managed-integrations", + "/api/managed-integrations#bank-account", + "/api/managed-integrations#contents", + "/api/managed-integrations#create-or-update-a-managed-integration-experimental", + "/api/managed-integrations#get-a-managed-integration-experimental", + "/api/managed-integrations#invitation-summary", + "/api/managed-integrations#invitation-summary-experimental", + "/api/managed-integrations#managed-integration", + "/api/managed-integrations#managed-integration-1", + "/api/managed-integrations#models", + "/api/managed-integrations#operations", + "/api/managed-integrations#params", + "/api/managed-integrations#paypal-referral", + "/api/managed-integrations#top", + "/api/media-uploads", + "/api/media-uploads#contents", + "/api/media-uploads#create-a-presigned-url-for-media-upload-experimental", + "/api/media-uploads#get-media-upload-location-experimental", + "/api/media-uploads#media-upload", + "/api/media-uploads#media-uploads", + "/api/media-uploads#models", + "/api/media-uploads#operations", + "/api/media-uploads#top", + "/api/merchant-configs", + "/api/merchant-configs#contents", + "/api/merchant-configs#create-a-merchant-config", + "/api/merchant-configs#get-a-merchant-config", + "/api/merchant-configs#list-merchant-configs", + "/api/merchant-configs#merchant-config", + "/api/merchant-configs#merchant-configs", + "/api/merchant-configs#models", + "/api/merchant-configs#operations", + "/api/merchant-configs#payment-option-config", + "/api/merchant-configs#top", + "/api/merchant-configs#update-a-merchant-config", + "/api/merchants", + "/api/merchants#accepted-asset", + "/api/merchants#contents", + "/api/merchants#create-a-merchant", + "/api/merchants#get-a-merchant", + "/api/merchants#list-all-merchants-deprecated", + "/api/merchants#list-merchants-for-account", + "/api/merchants#merchant", + "/api/merchants#merchant-search-result", + "/api/merchants#merchants", + "/api/merchants#models", + "/api/merchants#onboarding-status-reasons", + "/api/merchants#onboarding-statuses", + "/api/merchants#operations", + "/api/merchants#product", + "/api/merchants#search-merchants-experimental", + "/api/merchants#set-merchant-onboarding-status", + "/api/merchants#settlement-config", + "/api/merchants#top", + "/api/merchants#update-a-merchant", + "/api/pagination", + "/api/pagination#example", + "/api/pagination#models", + "/api/pagination#pagination", + "/api/pagination#top", + "/api/patron-codes", + "/api/patron-codes#contents", + "/api/patron-codes#creating-a-patron-code", + "/api/patron-codes#models", + "/api/patron-codes#operations", + "/api/patron-codes#patron-code", + "/api/patron-codes#patron-codes", + "/api/patron-codes#retrieving-a-patron-code", + "/api/patron-codes#retrieving-a-patron-code-by-barcode", + "/api/patron-codes#test-scenario-name", + "/api/patron-codes#top", + "/api/payment-activities", + "/api/payment-requests", + "/api/payment-requests#accept-payment-condition", + "/api/payment-requests#accept-payment-condition-for-a-payment-request", + "/api/payment-requests#accepted-collection", + "/api/payment-requests#asset-total", + "/api/payment-requests#cancellation-reasons", + "/api/payment-requests#contents", + "/api/payment-requests#create-a-payment-request", + "/api/payment-requests#decline-payment-condition-for-a-payment-request", + "/api/payment-requests#get-a-payment-request", + "/api/payment-requests#get-a-payment-request-by-short-code", + "/api/payment-requests#get-a-payment-request-linked-to-a-patron-code", + "/api/payment-requests#line-item", + "/api/payment-requests#list-payment-activities-for-a-merchant", + "/api/payment-requests#list-payment-activities-for-a-payment-request", + "/api/payment-requests#make-a-confirmation-against-a-pre-auth-payment-request", + "/api/payment-requests#models", + "/api/payment-requests#operations", + "/api/payment-requests#paid-by", + "/api/payment-requests#pay", + "/api/payment-requests#pay-a-payment-request", + "/api/payment-requests#payment-activity", + "/api/payment-requests#payment-condition", + "/api/payment-requests#payment-option", + "/api/payment-requests#payment-request", + "/api/payment-requests#payment-requests", + "/api/payment-requests#product-classification", + "/api/payment-requests#refund", + "/api/payment-requests#refund-a-payment-request", + "/api/payment-requests#release-funds-held-for-a-pre-auth-payment-request", + "/api/payment-requests#top", + "/api/payment-requests#void-a-payment-request", + "/api/profiles", + "/api/profiles#contents", + "/api/profiles#get-profile-experimental", + "/api/profiles#models", + "/api/profiles#operations", + "/api/profiles#profile", + "/api/profiles#profile-1", + "/api/profiles#top", + "/api/profiles#update-a-profile-experimental", + "/api/quotas", + "/api/quotas#contents", + "/api/quotas#getting-account-quotas-experimental", + "/api/quotas#models", + "/api/quotas#operations", + "/api/quotas#quota", + "/api/quotas#quotas", + "/api/quotas#top", + "/api/scanned-codes", + "/api/scanned-codes#contents", + "/api/scanned-codes#decode-scanned-code", + "/api/scanned-codes#models", + "/api/scanned-codes#operations", + "/api/scanned-codes#scanned-code", + "/api/scanned-codes#scanned-codes", + "/api/scanned-codes#top", + "/api/section/accounts", + "/api/section/assets", + "/api/section/bank-accounts", + "/api/section/batches", + "/api/section/integrations", + "/api/section/merchants", + "/api/section/payment-requests", + "/api/section/scanned-codes", + "/api/section/settlements", + "/api/settlements", + "/api/settlements#contents", + "/api/settlements#list-settlements", + "/api/settlements#models", + "/api/settlements#operations", + "/api/settlements#settlement", + "/api/settlements#settlements", + "/api/settlements#top", + "/api/tokens", + "/api/tokens#allowed-products-experimental", + "/api/tokens#allowedProducts", + "/api/tokens#contents", + "/api/tokens#create-redemption-condition-experimental", + "/api/tokens#create-token-collection-experimental", + "/api/tokens#create-token-experimental", + "/api/tokens#list-token-collections-for-account-experimental", + "/api/tokens#models", + "/api/tokens#operations", + "/api/tokens#redemption-condition-experimental", + "/api/tokens#token-collection", + "/api/tokens#token-collection-experimental", + "/api/tokens#token-expires-after-experimental", + "/api/tokens#tokenExpiresAfter", + "/api/tokens#tokens", + "/api/tokens#top", + "/api/transacting", + "/api/wallets", + "/api/wallets#contents", + "/api/wallets#creating-a-wallet", + "/api/wallets#listing-authorized-wallets", + "/api/wallets#listing-wallet-transactions-experimental", + "/api/wallets#settlement-wallets", + "/api/wallets#settlement-wallets-experimental", + "/api/wallets#top", + "/api/wallets#wallets", + "/assets", + "/assets/asset-categories", + "/assets/asset-transfers", + "/assets/discrete-assets", + "/assets/wallets", + "/auth", + "/connections/farmlands", + "/fiat", + "/fiat/authorities", + "/fiat/bank-accounts", + "/fiat/bank-authorities", + "/fiat/funds-transfers", + "/guides/creating-digital-tokens", + "/guides/creating-digital-tokens#implementation", + "/guides/creating-digital-tokens#redemption-conditions", + "/guides/creating-digital-tokens#token-collection", + "/guides/creating-test-money", + "/guides/creating-test-money#via-api", + "/guides/creating-test-money#via-centrapay-app", + "/guides/ecommerce-website", + "/guides/example-oidc-consumer", + "/guides/example-oidc-consumer#detect-oidc-user-refresh", + "/guides/example-oidc-consumer#minimal-oidc-client-usage", + "/guides/example-oidc-consumer#oidc-client-logout", + "/guides/example-oidc-consumer#oidc-user-manager-configuration", + "/guides/example-oidc-consumer#trigger-token-refresh", + "/guides/farmlands-card-partner-support", + "/guides/farmlands-card-partner-support#p1---severe-business-disruption", + "/guides/farmlands-card-partner-support#p2---major-business-disruption", + "/guides/farmlands-card-partner-support#p3---minor-business-disruption", + "/guides/farmlands-card-partner-support#p4---adhoc-requests-as-requested-by-farmlands", + "/guides/farmlands-card-partner-support#sla-support-level", + "/guides/farmlands-card-partner-support#support-for-portal-users", + "/guides/farmlands-card-partner-support#support-for-pos-integrators", + "/guides/farmlands-card-partner-support#trouble-shooting", + "/guides/farmlands-portal", + "/guides/farmlands-portal#authorisation-history", + "/guides/farmlands-portal#authorisation-responses", + "/guides/farmlands-portal#bank-accounts", + "/guides/farmlands-portal#business-information", + "/guides/farmlands-portal#create-a-farmlands-authorisation", + "/guides/farmlands-portal#dashboard", + "/guides/farmlands-portal#faqs", + "/guides/farmlands-portal#farmlands-authorisations", + "/guides/farmlands-portal#getting-started", + "/guides/farmlands-portal#invoicing--settlement", + "/guides/farmlands-portal#log-in", + "/guides/farmlands-portal#manage-my-centrapay-business-account", + "/guides/farmlands-portal#members", + "/guides/farmlands-portal#merchants", + "/guides/farmlands-portal#registration", + "/guides/farmlands-portal#release-hold-on-customer-funds", + "/guides/farmlands-portal#releasing-funds", + "/guides/farmlands-portal#setting-a-default-merchant", + "/guides/farmlands-portal#settings", + "/guides/farmlands-portal#support", + "/guides/farmlands-pos-integration", + "/guides/farmlands-pos-integration#authentication", + "/guides/farmlands-pos-integration#basic-requirements", + "/guides/farmlands-pos-integration#cardholder-not-present", + "/guides/farmlands-pos-integration#centrapay-integration-benefits", + "/guides/farmlands-pos-integration#certification-requirements", + "/guides/farmlands-pos-integration#handling-payment-errors", + "/guides/farmlands-pos-integration#merchant-configuration", + "/guides/farmlands-pos-integration#next-steps", + "/guides/farmlands-pos-integration#payment-conditions", + "/guides/farmlands-pos-integration#payment-flow-implementation", + "/guides/farmlands-pos-integration#payment-flow-overview", + "/guides/farmlands-pos-integration#pre-auth", + "/guides/farmlands-pos-integration#refunds", + "/guides/farmlands-pos-integration#short-codes", + "/guides/farmlands-pos-integration#testing-your-integration", + "/guides/farmlands-pos-integration#validating-farmlands-barcodes", + "/guides/initiating-refunds", + "/guides/initiating-refunds#by-short-code", + "/guides/initiating-refunds#by-transaction-reference", + "/guides/initiating-refunds#multi-asset-selection", + "/guides/initiating-refunds#obtaining-a-payment-request-id", + "/guides/initiating-refunds#refund-error-handling", + "/guides/initiating-refunds#refund-idempotency", + "/guides/integrating-third-party-asset", + "/guides/integrating-third-party-asset#authentication", + "/guides/integrating-third-party-asset#cancel-endpoint", + "/guides/integrating-third-party-asset#contact-details", + "/guides/integrating-third-party-asset#defining-a-payment-method", + "/guides/integrating-third-party-asset#description", + "/guides/integrating-third-party-asset#error-codes", + "/guides/integrating-third-party-asset#example-definition", + "/guides/integrating-third-party-asset#failure-reasons", + "/guides/integrating-third-party-asset#get-transaction-endpoint", + "/guides/integrating-third-party-asset#implementation-checklist", + "/guides/integrating-third-party-asset#liveness", + "/guides/integrating-third-party-asset#namespace", + "/guides/integrating-third-party-asset#pay-endpoint", + "/guides/integrating-third-party-asset#protocol", + "/guides/integrating-third-party-asset#refund-endpoint", + "/guides/integrating-third-party-asset#settlement", + "/guides/integrating-third-party-asset#statuses", + "/guides/integrating-third-party-asset#supported-currencies", + "/guides/integrating-third-party-asset#transaction-attempt-model", + "/guides/integrating-third-party-asset#transaction-idempotency", + "/guides/integrating-third-party-asset#uplink-api-requirements", + "/guides/integrating-third-party-asset#uplink-api-spec", + "/guides/integrating-third-party-asset#url", + "/guides/line-items", + "/guides/line-items#determining-paid-for-line-items", + "/guides/line-items#restrictions", + "/guides/loading-and-sending-assets", + "/guides/loading-and-sending-assets#example-bulk-distribution-of-giftcards", + "/guides/loading-and-sending-assets#loading-giftcards", + "/guides/loading-and-sending-assets#sending-assets", + "/guides/merchant-integration-barcode-flow", + "/guides/merchant-integration-barcode-flow#barcode-flow", + "/guides/merchant-integration-barcode-flow#checking-barcode-details", + "/guides/merchant-integration-barcode-flow#quick-pay-flow", + "/guides/merchant-integration-error-handling", + "/guides/merchant-integration-error-handling#configure-pos-timeout", + "/guides/merchant-integration-error-handling#resolving-persistent-errors", + "/guides/merchant-integration-error-handling#respect-payment-status", + "/guides/merchant-integration-error-handling#retry-unknown-errors", + "/guides/merchant-integration-error-handling#void-unknown-status", + "/guides/merchant-integration-qr-code-flow", + "/guides/merchant-terminals", + "/guides/partial-payment-extension", + "/guides/partial-payment-extension#creating-the-payment-request", + "/guides/partial-payment-extension#hybrid-partial-payment-scenario", + "/guides/partial-payment-extension#implementation", + "/guides/partial-payment-extension#merchant-driven-scenario", + "/guides/partial-payment-extension#partial-payment-flow", + "/guides/partial-payment-extension#patron-driven-scenario", + "/guides/partial-payment-extension#polling-for-payment-confirmation", + "/guides/partial-payment-extension#see-also", + "/guides/patron-not-present", + "/guides/payment-conditions", + "/guides/payment-conditions#additional-behaviors", + "/guides/payment-conditions#implementation", + "/guides/payment-flows", + "/guides/payment-flows#dynamic-merchant-qr-code", + "/guides/payment-flows#dynamic-patron-barcode", + "/guides/payment-flows#merchant-presented", + "/guides/payment-flows#patron-presented", + "/guides/payment-flows#quick-pay", + "/guides/payment-flows#static-merchant-qr-code", + "/guides/payment-flows#static-patron-barcode", + "/guides/payment-terminals", + "/guides/payment-terminals#api-keys", + "/guides/payment-terminals#displaying-a-button", + "/guides/payment-terminals#displaying-an-icon", + "/guides/payment-terminals#displaying-qr-codes", + "/guides/payment-terminals#example-flows", + "/guides/payment-terminals#integration-architecture", + "/guides/payment-terminals#merchant-configs", + "/guides/payment-terminals#mitigating-network-issues", + "/guides/payment-terminals#terminal-interface-guidelines", + "/guides/point-of-sale", + "/guides/point-of-sale#contact-us", + "/guides/point-of-sale#core-requirements", + "/guides/point-of-sale#optional-protocol-extensions", + "/guides/requesting-payment", + "/guides/requesting-payment#connecting-with-patrons", + "/guides/requesting-payment#handling-payment-errors", + "/guides/requesting-payment#polling-for-payment-confirmation", + "/guides/requesting-payment#protocol-extensions", + "/guides/requesting-payment#required-fields", + "/guides/requesting-payment#short-codes", + "/guides/requesting-pre-auth", + "/guides/requesting-pre-auth#authorize", + "/guides/requesting-pre-auth#confirm", + "/guides/requesting-pre-auth#expiry", + "/guides/requesting-pre-auth#pre-auth-flow", + "/guides/requesting-pre-auth#refund", + "/guides/requesting-pre-auth#release", + "/guides/requesting-pre-auth#restrictions", + "/guides/requesting-pre-auth#void", + "/guides/third-party-application-payments", + "/guides/third-party-application-payments#authenticating-api-calls-on-behalf-of-users", + "/guides/third-party-application-payments#get-the-payment-request", + "/guides/third-party-application-payments#overview", + "/guides/third-party-application-payments#partial-pay", + "/guides/third-party-application-payments#pay-the-payment-request", + "/guides/third-party-application-payments#payment-extensions", + "/guides/third-party-application-payments#payment-flow-implementation", + "/guides/third-party-application-payments#payment-flow-overview", + "/guides/third-party-application-payments#scan-centrapay-qr-code", + "/guides/third-party-application-payments#testing", + "/guides/transaction-reporting", + "/merchants", + "/profile", + "/quotas", + "/settlements", + "/transacting", +] +`; diff --git a/src/__tests__/links.integration.test.js b/src/__tests__/links.integration.test.js new file mode 100644 index 000000000..2e94157d8 --- /dev/null +++ b/src/__tests__/links.integration.test.js @@ -0,0 +1,40 @@ +/* eslint-disable complexity */ +const fs = require('fs'); +const path = require('path'); +const { JSDOM } = require('jsdom'); + +function extractAnchors(filePath, urlPath) { + const fileContents = fs.readFileSync(filePath, 'utf-8'); + const dom = new JSDOM(fileContents); + const links = Array.from(dom.window.document.querySelectorAll('a')) + .map(link => link.href) + .filter(href => href && href.startsWith('about:blank#') && href.length > 12) + .map(href => `${urlPath}${href.split('about:blank')[1]}`); + return links; +} + +function extractLinksRecursively(directory) { + const files = fs.readdirSync(directory); + let links = new Set(); + for (const file of files) { + const filePath = path.join(directory, file); + const stat = fs.statSync(filePath); + if (stat.isDirectory()) { + links = new Set([...links, ...extractLinksRecursively(filePath)]); + } else if (stat.isFile() && filePath.endsWith('index.html')) { + const urlPath = filePath.substring(filePath.indexOf('/'), filePath.lastIndexOf('/')); + links = new Set([...links, urlPath, ...extractAnchors(filePath, urlPath)]); + } else if (stat.isFile() && filePath.endsWith('.html')) { + const urlPath = filePath.substring(filePath.indexOf('/'), filePath.lastIndexOf('.')); + links = new Set([...links, urlPath, ...extractAnchors(filePath, urlPath)]); + } + } + return links; +} + +test('the build should not break any links', () => { + const uniqueLinks = extractLinksRecursively('_site'); + const sortedLinks = Array.from(uniqueLinks).sort(); + expect(sortedLinks).toMatchSnapshot(); +}); + diff --git a/vitest.config.js b/vitest.config.js index aacfc72fc..1fb158faa 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -2,6 +2,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - globals: true + globals: true, + exclude: ['**/node_modules/**', '**/__tests__/*.integration.test.js'] }, }); diff --git a/vitest.integration.config.js b/vitest.integration.config.js new file mode 100644 index 000000000..05b4bc1c7 --- /dev/null +++ b/vitest.integration.config.js @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + include: ['**/__tests__/*.integration.test.js'] + }, +}); diff --git a/yarn.lock b/yarn.lock index cae7139cc..27bb4c033 100644 --- a/yarn.lock +++ b/yarn.lock @@ -940,6 +940,11 @@ lodash.merge "^4.6.2" postcss-selector-parser "6.0.10" +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@types/acorn@^4.0.0": version "4.0.6" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" @@ -1290,6 +1295,11 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.4.tgz#06e83c5027f464eef861c329be81454bc8b70780" integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + acorn-jsx@^5.0.0, acorn-jsx@^5.2.0, acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1541,6 +1551,11 @@ astro@^3.0.12: optionalDependencies: sharp "^0.32.5" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.15: version "10.4.15" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.15.tgz#a1230f4aeb3636b89120b34a1f513e2f6834d530" @@ -1873,6 +1888,13 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -1917,6 +1939,13 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== + dependencies: + rrweb-cssom "^0.6.0" + csstype@^2.6.8: version "2.6.21" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" @@ -2210,6 +2239,15 @@ dagre-d3-es@7.0.6: d3 "^7.7.0" lodash-es "^4.17.21" +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" + debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -2231,6 +2269,11 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" @@ -2282,6 +2325,11 @@ delaunator@5: dependencies: robust-predicates "^3.0.0" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -2349,6 +2397,13 @@ domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -2414,7 +2469,7 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" -entities@^4.2.0, entities@^4.5.0: +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -2980,6 +3035,15 @@ flexsearch@^0.7.31: resolved "https://registry.yarnpkg.com/flexsearch/-/flexsearch-0.7.31.tgz#065d4110b95083110b9b6c762a71a77cc52e4702" integrity sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -3333,6 +3397,13 @@ hastscript@^7.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6" @@ -3363,7 +3434,16 @@ http-cache-semantics@^4.1.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -https-proxy-agent@^5.0.0: +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -3376,7 +3456,7 @@ human-signals@^5.0.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== -iconv-lite@0.6: +iconv-lite@0.6, iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -3429,6 +3509,11 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -3593,6 +3678,11 @@ is-plain-obj@^4.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-reference@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.1.tgz#d400f4260f7e55733955e60d361d827eb4d3b831" @@ -3683,6 +3773,35 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsdom@^22.1.0: + version "22.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" + integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== + dependencies: + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -4517,6 +4636,18 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" @@ -4701,6 +4832,11 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +nwsapi@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== + object-hash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" @@ -4858,6 +4994,13 @@ parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -4896,6 +5039,14 @@ path-to-regexp@^6.2.1: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + pathe@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.0.0.tgz#135fc11464fc57c84ef93d5c5ed21247e24571df" @@ -5090,6 +5241,11 @@ prismjs@^1.29.0: resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== +process@^0.11.1: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -5108,6 +5264,11 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22" integrity sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w== +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -5121,6 +5282,16 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^2.1.1, punycode@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -5323,6 +5494,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -5424,6 +5600,11 @@ rollup@^3.7.0: optionalDependencies: fsevents "~2.3.2" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -5462,6 +5643,13 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + section-matter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" @@ -5836,6 +6024,11 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + table@^6.0.9: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" @@ -5946,6 +6139,23 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== + dependencies: + punycode "^2.3.0" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -6176,6 +6386,11 @@ unist-util-visit@^4.1.2: unist-util-is "^5.0.0" unist-util-visit-parents "^5.1.1" +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" @@ -6191,11 +6406,26 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + uuid@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" @@ -6358,6 +6588,13 @@ vue@^3.2.47: "@vue/server-renderer" "3.2.47" "@vue/shared" "3.2.47" +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + web-namespaces@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" @@ -6368,6 +6605,31 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -6449,6 +6711,21 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +ws@^8.13.0: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"