From 492c3103047d90d81ead49f9a8bac19b2fcfdc5a Mon Sep 17 00:00:00 2001 From: Gabriel P Samson Date: Tue, 6 Jul 2021 12:10:37 -0700 Subject: [PATCH] Open source release --- .editorconfig | 10 + .eslintrc.js | 72 + .gitattributes | 1 + .github/workflows/ci.yml | 43 + .gitignore | 5 + .nvmrc | 1 + .vscode/extensions.json | 3 + .vscode/launch.json | 46 + .vscode/settings.json | 9 + README.md | 111 + bin | 1 + lerna.json | 6 + package.json | 71 + packages/ajv-human-errors/README.md | 186 + packages/ajv-human-errors/index.js | 264 + packages/ajv-human-errors/index.test.js | 489 + packages/ajv-human-errors/package.json | 19 + packages/ajv-human-errors/util.js | 80 + packages/ajv-human-errors/util.test.js | 66 + packages/cli/bin/run | 24 + packages/cli/bin/run.cmd | 3 + packages/cli/package.json | 100 + packages/cli/src/__tests__/init.test.ts | 70 + packages/cli/src/commands/generate/action.ts | 147 + packages/cli/src/commands/generate/types.ts | 156 + packages/cli/src/commands/init.ts | 142 + packages/cli/src/commands/push.ts | 474 + packages/cli/src/commands/register.ts | 152 + packages/cli/src/constants.ts | 83 + packages/cli/src/index.ts | 1 + packages/cli/src/lib/codemods.ts | 67 + packages/cli/src/lib/control-plane-service.ts | 16 + packages/cli/src/lib/destinations.ts | 24 + packages/cli/src/lib/prompt.ts | 44 + packages/cli/src/lib/require-cache.ts | 5 + packages/cli/src/lib/templates.ts | 31 + .../templates/actions/empty-action/index.ts | 15 + .../actions/empty-browser-action/index.ts | 15 + .../destinations/basic-auth/index.ts | 35 + .../templates/destinations/browser/index.ts | 22 + .../destinations/custom-auth/index.ts | 16 + .../templates/destinations/minimal/index.ts | 9 + .../destinations/oauth2-auth/index.ts | 37 + packages/cli/tsconfig.build.json | 23 + packages/cli/tsconfig.json | 29 + packages/core/benchmarks/fql.js | 35 + packages/core/benchmarks/mapping-kit.js | 47 + packages/core/package.json | 72 + .../__tests__/create-request-client.test.ts | 114 + .../src/__tests__/destination-kit.test.ts | 153 + packages/core/src/__tests__/get.test.ts | 43 + .../core/src/__tests__/request-client.test.ts | 453 + packages/core/src/create-request-client.ts | 37 + packages/core/src/create-test-event.ts | 43 + packages/core/src/create-test-integration.ts | 68 + packages/core/src/defaults.ts | 14 + packages/core/src/destination-kit/README.md | 198 + packages/core/src/destination-kit/action.ts | 369 + .../destination-kit/fields-to-jsonschema.ts | 85 + packages/core/src/destination-kit/index.ts | 356 + packages/core/src/destination-kit/step.ts | 82 + packages/core/src/destination-kit/types.ts | 93 + packages/core/src/errors.ts | 70 + packages/core/src/fetch.ts | 5 + packages/core/src/get.ts | 21 + packages/core/src/index.ts | 37 + packages/core/src/json-object.ts | 11 + packages/core/src/map-values.ts | 16 + packages/core/src/mapping-kit/README.md | 433 + .../src/mapping-kit/__tests__/index.test.ts | 181 + .../__tests__/placeholders.test.ts | 70 + .../mapping-kit/__tests__/validate.test.ts | 55 + packages/core/src/mapping-kit/index.ts | 132 + packages/core/src/mapping-kit/is-directive.ts | 22 + packages/core/src/mapping-kit/placeholders.ts | 55 + .../invalid/@if-invalid-nested-field.json | 11 + .../invalid/@path-wrong-type.json | 6 + .../invalid/@template-wrong-type.json | 6 + .../invalid/mixed-directive-and-raw.json | 7 + .../invalid/multiple-directives.json | 7 + .../invalid/multiple-errors.json | 15 + .../invalid/unknown-directive.json | 6 + .../schema-fixtures/valid/@if-nested.json | 15 + .../schema-fixtures/valid/@if-partial.json | 8 + .../schema-fixtures/valid/@if-simple.json | 9 + .../schema-fixtures/valid/@path-nested.json | 7 + .../schema-fixtures/valid/@path-simple.json | 5 + .../valid/@template-metadata.json | 9 + .../valid/@template-nested.json | 7 + .../valid/@template-simple.json | 5 + .../schema-fixtures/valid/raw-array.json | 3 + .../valid/raw-object-nested.json | 7 + .../schema-fixtures/valid/raw-object.json | 6 + .../schema-fixtures/valid/raw-string.json | 3 + packages/core/src/mapping-kit/validate.ts | 246 + .../after-response/prepare-headers.ts | 25 + .../after-response/prepare-response.ts | 30 + .../before-request/add-basic-auth-header.ts | 19 + packages/core/src/omit.ts | 9 + packages/core/src/real-type-of.ts | 35 + packages/core/src/remove-undefined.ts | 23 + packages/core/src/request-client.ts | 312 + packages/core/src/segment-event.ts | 158 + packages/core/src/time.ts | 13 + packages/core/src/types.ts | 11 + packages/core/test/create-test-server.d.ts | 19 + packages/core/tsconfig.build.json | 8 + packages/core/tsconfig.json | 14 + packages/destination-actions/package.json | 52 + .../amplitude/__tests__/amplitude.test.ts | 227 + .../destinations/amplitude/event-schema.ts | 300 + .../destinations/amplitude/generated-types.ts | 12 + .../groupIdentifyUser/generated-types.ts | 34 + .../amplitude/groupIdentifyUser/index.ts | 112 + .../amplitude/identifyUser/generated-types.ts | 88 + .../amplitude/identifyUser/index.ts | 179 + .../src/destinations/amplitude/index.ts | 90 + .../amplitude/logEvent/generated-types.ts | 183 + .../destinations/amplitude/logEvent/index.ts | 144 + .../amplitude/mapUser/generated-types.ts | 12 + .../destinations/amplitude/mapUser/index.ts | 39 + .../createUpdateDevice/generated-types.ts | 20 + .../customerio/createUpdateDevice/index.ts | 63 + .../createUpdatePerson/generated-types.ts | 22 + .../customerio/createUpdatePerson/index.ts | 60 + .../customerio/generated-types.ts | 12 + .../src/destinations/customerio/index.ts | 74 + .../trackAnonymousEvent/generated-types.ts | 14 + .../customerio/trackAnonymousEvent/index.ts | 38 + .../customerio/trackEvent/generated-types.ts | 22 + .../customerio/trackEvent/index.ts | 58 + .../triggerCampaign/generated-types.ts | 24 + .../customerio/triggerCampaign/index.ts | 83 + .../__tests__/addToCart.test.ts | 236 + .../__tests__/beginCheckout.test.ts | 258 + .../__tests__/customEvent.test.ts | 175 + .../__tests__/pageView.test.ts | 127 + .../__tests__/purchase.test.ts | 316 + .../__tests__/selectItem.test.ts | 206 + .../__tests__/selectPromotion.test.ts | 319 + .../__tests__/viewItem.test.ts | 182 + .../addToCart/generated-types.ts | 77 + .../google-analytics-4/addToCart/index.ts | 157 + .../beginCheckout/generated-types.ts | 69 + .../google-analytics-4/beginCheckout/index.ts | 160 + .../google-analytics-4/constants.ts | 181 + .../customEvent/generated-types.ts | 18 + .../google-analytics-4/customEvent/index.ts | 52 + .../google-analytics-4/ga4-types.ts | 18 + .../google-analytics-4/generated-types.ts | 12 + .../destinations/google-analytics-4/index.ts | 58 + .../pageView/generated-types.ts | 16 + .../google-analytics-4/pageView/index.ts | 59 + .../purchase/generated-types.ts | 89 + .../google-analytics-4/purchase/index.ts | 205 + .../selectItem/generated-types.ts | 77 + .../google-analytics-4/selectItem/index.ts | 154 + .../selectPromotion/generated-types.ts | 81 + .../selectPromotion/index.ts | 167 + .../viewItem/generated-types.ts | 65 + .../google-analytics-4/viewItem/index.ts | 161 + .../google-enhanced-conversions.test.ts | 57 + .../generated-types.ts | 8 + .../google-enhanced-conversions/index.ts | 35 + .../postConversion/formatter.ts | 51 + .../postConversion/generated-types.ts | 64 + .../postConversion/index.ts | 229 + .../src/destinations/index.ts | 73 + .../generated-types.ts | 20 + .../createUpdateOrganization/index.ts | 77 + .../createUpdatePerson/generated-types.ts | 28 + .../pipedrive/createUpdatePerson/index.ts | 144 + .../destinations/pipedrive/generated-types.ts | 12 + .../src/destinations/pipedrive/index.ts | 46 + .../src/destinations/slack/generated-types.ts | 3 + .../src/destinations/slack/index.ts | 12 + .../slack/postToChannel/generated-types.ts | 24 + .../destinations/slack/postToChannel/index.ts | 55 + .../twilio/__tests__/twilio.test.ts | 78 + .../destinations/twilio/generated-types.ts | 16 + .../src/destinations/twilio/index.ts | 44 + .../twilio/sendSms/generated-types.ts | 12 + .../src/destinations/twilio/sendSms/index.ts | 37 + packages/destination-actions/src/index.ts | 17 + packages/destination-actions/src/lib/dayjs.ts | 6 + .../test/setup-after-env.ts | 17 + .../destination-actions/tsconfig.build.json | 8 + packages/destination-actions/tsconfig.json | 11 + .../destination-subscriptions/package.json | 75 + .../src/__tests__/fql.test.ts | 497 + .../src/__tests__/generate-fql.test.ts | 105 + .../src/__tests__/validate.test.ts | 702 + .../src/generate-fql.ts | 119 + packages/destination-subscriptions/src/get.ts | 25 + .../destination-subscriptions/src/index.ts | 4 + .../src/parse-fql.ts | 274 + .../destination-subscriptions/src/types.ts | 65 + .../destination-subscriptions/src/validate.ts | 90 + .../tsconfig.build.json | 8 + .../tsconfig.cjs.json | 7 + .../destination-subscriptions/tsconfig.json | 12 + tsconfig.eslint.json | 4 + tsconfig.json | 20 + yarn.lock | 13706 ++++++++++++++++ 204 files changed, 30211 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 120000 bin create mode 100644 lerna.json create mode 100644 package.json create mode 100644 packages/ajv-human-errors/README.md create mode 100644 packages/ajv-human-errors/index.js create mode 100644 packages/ajv-human-errors/index.test.js create mode 100644 packages/ajv-human-errors/package.json create mode 100644 packages/ajv-human-errors/util.js create mode 100644 packages/ajv-human-errors/util.test.js create mode 100755 packages/cli/bin/run create mode 100644 packages/cli/bin/run.cmd create mode 100644 packages/cli/package.json create mode 100644 packages/cli/src/__tests__/init.test.ts create mode 100644 packages/cli/src/commands/generate/action.ts create mode 100644 packages/cli/src/commands/generate/types.ts create mode 100644 packages/cli/src/commands/init.ts create mode 100644 packages/cli/src/commands/push.ts create mode 100644 packages/cli/src/commands/register.ts create mode 100644 packages/cli/src/constants.ts create mode 100644 packages/cli/src/index.ts create mode 100644 packages/cli/src/lib/codemods.ts create mode 100644 packages/cli/src/lib/control-plane-service.ts create mode 100644 packages/cli/src/lib/destinations.ts create mode 100644 packages/cli/src/lib/prompt.ts create mode 100644 packages/cli/src/lib/require-cache.ts create mode 100644 packages/cli/src/lib/templates.ts create mode 100644 packages/cli/templates/actions/empty-action/index.ts create mode 100644 packages/cli/templates/actions/empty-browser-action/index.ts create mode 100644 packages/cli/templates/destinations/basic-auth/index.ts create mode 100644 packages/cli/templates/destinations/browser/index.ts create mode 100644 packages/cli/templates/destinations/custom-auth/index.ts create mode 100644 packages/cli/templates/destinations/minimal/index.ts create mode 100644 packages/cli/templates/destinations/oauth2-auth/index.ts create mode 100644 packages/cli/tsconfig.build.json create mode 100644 packages/cli/tsconfig.json create mode 100644 packages/core/benchmarks/fql.js create mode 100644 packages/core/benchmarks/mapping-kit.js create mode 100644 packages/core/package.json create mode 100644 packages/core/src/__tests__/create-request-client.test.ts create mode 100644 packages/core/src/__tests__/destination-kit.test.ts create mode 100644 packages/core/src/__tests__/get.test.ts create mode 100644 packages/core/src/__tests__/request-client.test.ts create mode 100644 packages/core/src/create-request-client.ts create mode 100644 packages/core/src/create-test-event.ts create mode 100644 packages/core/src/create-test-integration.ts create mode 100644 packages/core/src/defaults.ts create mode 100644 packages/core/src/destination-kit/README.md create mode 100644 packages/core/src/destination-kit/action.ts create mode 100644 packages/core/src/destination-kit/fields-to-jsonschema.ts create mode 100644 packages/core/src/destination-kit/index.ts create mode 100644 packages/core/src/destination-kit/step.ts create mode 100644 packages/core/src/destination-kit/types.ts create mode 100644 packages/core/src/errors.ts create mode 100644 packages/core/src/fetch.ts create mode 100644 packages/core/src/get.ts create mode 100644 packages/core/src/index.ts create mode 100644 packages/core/src/json-object.ts create mode 100644 packages/core/src/map-values.ts create mode 100644 packages/core/src/mapping-kit/README.md create mode 100644 packages/core/src/mapping-kit/__tests__/index.test.ts create mode 100644 packages/core/src/mapping-kit/__tests__/placeholders.test.ts create mode 100644 packages/core/src/mapping-kit/__tests__/validate.test.ts create mode 100644 packages/core/src/mapping-kit/index.ts create mode 100644 packages/core/src/mapping-kit/is-directive.ts create mode 100644 packages/core/src/mapping-kit/placeholders.ts create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/@if-invalid-nested-field.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/@path-wrong-type.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/@template-wrong-type.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/mixed-directive-and-raw.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-directives.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-errors.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/invalid/unknown-directive.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@if-nested.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@if-partial.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@if-simple.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@path-nested.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@path-simple.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@template-metadata.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@template-nested.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/@template-simple.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/raw-array.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/raw-object-nested.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/raw-object.json create mode 100644 packages/core/src/mapping-kit/schema-fixtures/valid/raw-string.json create mode 100644 packages/core/src/mapping-kit/validate.ts create mode 100644 packages/core/src/middleware/after-response/prepare-headers.ts create mode 100644 packages/core/src/middleware/after-response/prepare-response.ts create mode 100644 packages/core/src/middleware/before-request/add-basic-auth-header.ts create mode 100644 packages/core/src/omit.ts create mode 100644 packages/core/src/real-type-of.ts create mode 100644 packages/core/src/remove-undefined.ts create mode 100644 packages/core/src/request-client.ts create mode 100644 packages/core/src/segment-event.ts create mode 100644 packages/core/src/time.ts create mode 100644 packages/core/src/types.ts create mode 100644 packages/core/test/create-test-server.d.ts create mode 100644 packages/core/tsconfig.build.json create mode 100644 packages/core/tsconfig.json create mode 100644 packages/destination-actions/package.json create mode 100644 packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/event-schema.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/index.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/identifyUser/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/identifyUser/index.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/index.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/logEvent/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/logEvent/index.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/mapUser/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/amplitude/mapUser/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/createUpdateDevice/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/createUpdateDevice/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/createUpdatePerson/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/createUpdatePerson/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/trackEvent/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/trackEvent/index.ts create mode 100644 packages/destination-actions/src/destinations/customerio/triggerCampaign/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/customerio/triggerCampaign/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/addToCart.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/beginCheckout.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/customEvent.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/pageView.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/purchase.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectItem.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectPromotion.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/__tests__/viewItem.test.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/addToCart/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/addToCart/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/constants.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/customEvent/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/customEvent/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/ga4-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/pageView/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/pageView/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/purchase/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/purchase/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/selectItem/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/selectItem/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/index.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/viewItem/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-analytics-4/viewItem/index.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/google-enhanced-conversions.test.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/index.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/formatter.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts create mode 100644 packages/destination-actions/src/destinations/index.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/index.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/index.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/pipedrive/index.ts create mode 100644 packages/destination-actions/src/destinations/slack/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/slack/index.ts create mode 100644 packages/destination-actions/src/destinations/slack/postToChannel/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/slack/postToChannel/index.ts create mode 100644 packages/destination-actions/src/destinations/twilio/__tests__/twilio.test.ts create mode 100644 packages/destination-actions/src/destinations/twilio/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/twilio/index.ts create mode 100644 packages/destination-actions/src/destinations/twilio/sendSms/generated-types.ts create mode 100644 packages/destination-actions/src/destinations/twilio/sendSms/index.ts create mode 100644 packages/destination-actions/src/index.ts create mode 100644 packages/destination-actions/src/lib/dayjs.ts create mode 100644 packages/destination-actions/test/setup-after-env.ts create mode 100644 packages/destination-actions/tsconfig.build.json create mode 100644 packages/destination-actions/tsconfig.json create mode 100644 packages/destination-subscriptions/package.json create mode 100644 packages/destination-subscriptions/src/__tests__/fql.test.ts create mode 100644 packages/destination-subscriptions/src/__tests__/generate-fql.test.ts create mode 100644 packages/destination-subscriptions/src/__tests__/validate.test.ts create mode 100644 packages/destination-subscriptions/src/generate-fql.ts create mode 100644 packages/destination-subscriptions/src/get.ts create mode 100644 packages/destination-subscriptions/src/index.ts create mode 100644 packages/destination-subscriptions/src/parse-fql.ts create mode 100644 packages/destination-subscriptions/src/types.ts create mode 100644 packages/destination-subscriptions/src/validate.ts create mode 100644 packages/destination-subscriptions/tsconfig.build.json create mode 100644 packages/destination-subscriptions/tsconfig.cjs.json create mode 100644 packages/destination-subscriptions/tsconfig.json create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..4e9067c681 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +; editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..db25810775 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,72 @@ +module.exports = { + root: true, + ignorePatterns: ['node_modules', 'dist', 'templates'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2019, + project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], + tsconfigRootDir: __dirname + }, + env: { + node: true + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'prettier/@typescript-eslint', + 'prettier' + ], + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': 'allow-with-description', + 'ts-nocheck': true, + 'ts-check': false + } + ], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { varsIgnorePattern: '^_', argsIgnorePattern: '^_', ignoreRestSiblings: true } + ], + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/restrict-plus-operands': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/unbound-method': 'off' + }, + overrides: [ + { + files: ['*.test.ts', '**/test/**/*.ts'], + env: { + jest: true + }, + plugins: ['jest'], + rules: { + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-explicit-any': 'off' + } + }, + { + files: ['**/benchmarks/**/*.js'], + env: { + node: true + }, + rules: { + '@typescript-eslint/no-var-requires': 'off' + } + } + ] +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..41951694c2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/*/generated-types.ts linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..f9c4d04c3c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: CI + +on: [push] + +jobs: + test-and-build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x, 14.x] + + steps: + - uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + registry-url: 'https://registry.npmjs.org' + + - name: Install Dependencies + run: yarn install --frozen-lockfile --ignore-optional + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/node@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high --fail-on=upgradable --all-projects + + - name: Build + run: NODE_ENV=production yarn build + + - name: Lint + run: yarn lint + + - name: Test + run: yarn test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..1010e43aff --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +.DS_Store +*.log +*.tsbuildinfo diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..bb29b0b8c5 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +>=12.x diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..3286d0e0f6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["eamodio.gitlens", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..07303a6e1f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach to running server", + "restart": true, + "address": "localhost", + "port": 47082, + "skipFiles": ["/**"] + }, + // Launches Jest tests in debug mode + // Simply drop a breakpoint and wait for the debugger to pick it up + { + "type": "node", + "request": "launch", + "name": "Jest Current File", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["${relativeFile}", "--coverage=false"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "skipFiles": [ + // skip annoying `async_hooks` file for promises + "/**" + ] + }, + { + "type": "node", + "name": "Jest All", + "request": "launch", + "args": ["--runInBand"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "skipFiles": [ + // skip annoying `async_hooks` file for promises + "/**" + ] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..68c9fdf3c5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "typescript"], + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true + }, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/README.md b/README.md new file mode 100644 index 0000000000..16cb755240 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# Action Destinations + +Action Destinations are a way to build streaming destinations on Segment. + +## Local Development + +This project is a monorepo with multiple packages using Yarn Workspaces: + +- `packages/cli` - a set of command line tools for interacting with the repo +- `packages/core` - the core runtime engine for actions, including mapping-kit transforms +- `packages/destinations-actions` - an npm package for use in `integrations` + +``` +$ yarn install +``` + +## Test Actions Locally + +To test actions locally, you send a curl request. For example: + +```sh +curl --request POST \ + --url http://localhost:3000/actions/5f7dd8e302173ff732db5cc4 \ + --header 'content-type: application/cloudevents-batch+json' \ + --data '[ + { + "id": "dkjfksldfjiuhfenjk", + "source": "some-source", + "destination": "slack", + "data": { + "channel": "server", + "context": { + "library": { + "name": "unknown", + "version": "unknown" + } + }, + "event": "Example Event", + "integrations": {}, + "messageId": "api-1iI59hvBEtckNicjbfqG7VdjRw2", + "projectId": "29qHxXL9muph5R19vwAnDP", + "properties": { + "text": "Hello, from dev :blobcatwave:!" + }, + "receivedAt": "2020-10-01T19:55:15.068Z", + "timestamp": "2020-10-01T19:55:15.068Z", + "type": "track", + "userId": "sloth@segment.com", + "version": 2 + }, + "settings": { + "subscription": { + "mapping": { + "channel": "test-channel", + "url": "https://hooks.slack.com/services/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", + "text": { + "@template": "Event = {{event}}, properties.text = {{properties.text}}" + } + }, + "partnerAction": "postToChannel", + "subscribe": "type = \"track\"" + } + } + } +]' +``` + +## Configuring + +Action destinations are configured using a single Destination setting (`subscriptions`) that should contain a JSON blob of all subscriptions for the destination. The format should look like this: + +```js +[ + { + "subscribe": "", + "partnerAction": "", + + // See ./packages/core/src/mapping-kit/README.md for documentation. The keys in this object should match the `action.fields` + "mapping": { ... } + } +] +``` + +Here's a full example: + +```json +[ + { + "subscribe": "type = 'track'", + "partnerAction": "postToChannel", + "mapping": { + "text": { + "@template": "Tracked! event={{event}}, {{properties.text}}" + }, + "url": "https://hooks.slack.com/services/0HL7TC62R/0T276CRHL/8WvI6gEiE9ZqD47kWqYbfIhZ", + "channel": "test-channel" + } + }, + { + "subscribe": "type = 'identify'", + "partnerAction": "postToChannel", + "mapping": { + "text": { + "@template": "User identified! email={{email}}" + }, + "url": "https://hooks.slack.com/services/0HL7TC62R/0T276CRHL/8WvI6gEiE9ZqD47kWqYbfIhZ", + "channel": "test-channel" + } + } +] +``` diff --git a/bin b/bin new file mode 120000 index 0000000000..818365a998 --- /dev/null +++ b/bin @@ -0,0 +1 @@ +./packages/cli/bin/ \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000000..bcdca04159 --- /dev/null +++ b/lerna.json @@ -0,0 +1,6 @@ +{ + "packages": ["packages/*"], + "npmClient": "yarn", + "useWorkspaces": true, + "version": "independent" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..72e82b02ca --- /dev/null +++ b/package.json @@ -0,0 +1,71 @@ +{ + "name": "action-destinations", + "private": true, + "license": "UNLICENSED", + "workspaces": { + "packages": [ + "packages/*" + ] + }, + "engines": { + "node": ">=12" + }, + "scripts": { + "cloud": "yarn workspace @segment/destination-actions", + "cli": "yarn workspace @segment/actions-cli", + "core": "yarn workspace @segment/actions-core", + "bootstrap": "lerna bootstrap", + "build": "npx segment generate:types && lerna run build --stream", + "types": "npx segment generate:types", + "lint": "eslint '**/*.ts' --max-warnings=10", + "test": "lerna run test --stream", + "typecheck": "lerna run typecheck --stream" + }, + "devDependencies": { + "@types/jest": "^26.0.23", + "@typescript-eslint/eslint-plugin": "^4.14.0", + "@typescript-eslint/parser": "^4.14.0", + "eslint": "^7.25.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-jest": "^24.3.6", + "husky": "^4.3.8", + "jest": "^26.6.3", + "lerna": "^4.0.0", + "lint-staged": "^10.5.3", + "prettier": "^2.2.1", + "ts-jest": "^26.5.5", + "ts-node": "^9.1.1", + "typescript": "^4.1.3" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "**/src/destinations/**/index.ts": [ + "npx segment generate:types" + ], + "!(templates)/**/*.ts": [ + "eslint --fix", + "prettier --write" + ], + "*.{yml,md,json}": [ + "prettier --write" + ] + }, + "prettier": { + "semi": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 120 + }, + "jest": { + "preset": "ts-jest", + "globals": { + "ts-jest": { + "isolatedModules": true + } + } + } +} diff --git a/packages/ajv-human-errors/README.md b/packages/ajv-human-errors/README.md new file mode 100644 index 0000000000..1085bd7d2f --- /dev/null +++ b/packages/ajv-human-errors/README.md @@ -0,0 +1,186 @@ +# ajv-human-errors + +> Human-readable error messages for [Ajv](https://ajv.js.org) (Another JSON Schema Validator). + +By default, Ajv error messages leave a little bit to be desired. ajv-human-errors provides an +aggregate Error object that holds Ajv validation errors that are more easily readable by humans. For +example, ajv-human-errors changes "should NOT have additional properties" to "The root value has an +unexpected property, c, which is not in the list of allowed properties (a, d)." + +You can also override the error message entirely using the "errorMessage" schema keyword, like you +can with [ajv-errors](https://github.com/ajv-validator/ajv-errors) (see ["Schema +Options"](#schema-options)). + +The following Ajv options must be set to `true` for `ajv-human-errors` to work with the errors returned by +Ajv: + +- `allErrors` +- `verbose` +- `jsonPointers` + +The following features of JSON Schema are not yet implemented (but will return their "raw" Ajv error +messages): + +- patternProperties +- allOf +- oneOf +- Nested array schemas +- const +- if/then/else +- contentEncoding/contentMediaType + +# Install + +``` +$ npm install @segment/ajv-human-error +``` + +# Usage + +```js +const Ajv = require('ajv') +const { AggregateAjvError } = require('@segment/ajv-human-errors') + +const ajv = new Ajv({ + allErrors: true, + verbose: true, + jsonPointers: true +}) + +ajv.validate({ title: 'Bag of Bytes', type: 'string' }, 1234) + +const err = new AggregateAjvError(ajv.errors) +console.log(err.message) +// 'Bag of Bytes should be a string but it was a number.' +console.log([...err].map((e) => e.message)) +// ['Bag of Bytes should be a string but it was a number.'] +``` + +The `AggregateAjvError` object can be passed to `JSON.stringify()`: + +```json +[ + { + "message": "The value at .arr should be unique but elements 1 and 4 are the same.", + "path": "$.arr", + "pointer": "/arr", + "data": [0, 1, 2, 0, 1] + } + // ... +] +``` + +# API + +AggregateAjvError is an Iterable that yields AjvError errors: + +```js +const { AggregateAjvError } = require('@segment/ajv-human-errors') + +const err = new AggregateAjvError(ajv.errors) + +const messages = [...err].map((e) => e.message) +// or +const messages = [] +for (const e of err) { + messages.push(e.message) +} +``` + +Each AjvError instance has the following properties: + +- `pointer`: JSON Pointer to the field that this error refers to. + +- `path`: JSON Path to the field that this error refers to. + +- `message`: Human-readable error message. + +- `original`: (Only if `includeOriginalError` option is set) Original Ajv error object. + +- `data`: (Only if `includeData` option is set) Value that value that failed validation. Useful for + showing users what, exactly, was wrong without embedding entire values in the error message. + +These fields are also available in the JSON form: + +```js +const { AggregateAjvError } = require('@segment/ajv-human-errors') + +const err = new AggregateAjvError(ajv.errors) + +console.log([...err][0].toJSON()) +// { +// message: 'The value at /arr should be unique but elements 1 and 4 are the same.', +// path: '$.arr', +// pointer: '/arr', +// original: { ... }, +// data: [0, 1, 2, 0, 1] +// } +``` + +# Options + +The `AggregateAjvError` constructor accepts the following options: + +- `fieldLabels` (default: `'title'`) Change the labels used for fields. Allowed values: + + - `'js'` JavaScript object accessor notation. Example: "The value at .for.bar should be a number + but it was a string." + + - `'jsonPath'` [JSON Path](https://goessner.net/articles/JsonPath/) notation. Example: "The + value at $.foo.bar should be a number but it was a string." + + - `'jsonPointer'` [JSON Pointer](https://tools.ietf.org/html/rfc6901) notation. Example: "The + value at /foo/bar should be a number but it was a string." + + - `'title'` Uses the `title` property of the schema rule that failed validation. If your schema + is: + + { + "title": "Bag of values", + "type": "object" + } + + Then the resulting error message would look like: "Bag of values should be an object but it + was an array." + +- `includeOriginalError` (default: false) Include the original Ajv error object on the `data` + property of each error in the `AggregateAjvError` instance: + + ``` + const err = new AggregateAjvError(ajv.errors, { includeOriginalError: true }) + [...err].forEach(e => console.log(e.original)) + // { + // params: { ... }, + // parentSchema: { ... }, + // schema: '...', + // schemaPath: '...', + // ... + // } + ``` + +- `includeData` (default: false) Include the value of the field that failed validation on the `data` + property of each error in the `AggregateAjvError` instance. + + ``` + const err = new AggregateAjvError(ajv.errors, { includeOriginalError: true }) + [...err].forEach(e => console.log(e.data)) + // 'foobar' + ``` + +# Schema Options + +If you want to override an error message entirely, you can set an "errorMessage" keyword in your +JSON Schema. For example, this schema: + +```json +{ + "type": "string", + "errorMessage": "should be a bag of bytes" +} +``` + +Returns this error message when validating a non-string object: + +```js +'The root value should be a bag of bytes.' +``` diff --git a/packages/ajv-human-errors/index.js b/packages/ajv-human-errors/index.js new file mode 100644 index 0000000000..54ccd5f473 --- /dev/null +++ b/packages/ajv-human-errors/index.js @@ -0,0 +1,264 @@ +const util = require('./util') + +const defaultOpts = { + fieldLabels: 'title', // js, jsonPath, jsonPointer, title + includeOriginalError: false, + includeData: false +} + +const formatLabels = { + 'date-time': 'date and time', + time: 'time', + date: 'date', + email: 'email address', + hostname: 'hostname', + ipv4: 'IPv4 address', + ipv6: 'IPv6 address', + uri: 'URI', + 'uri-reference': 'URI Reference', + 'uri-template': 'URI-template', + 'json-pointer': 'JSON Pointer', + 'relative-json-pointer': 'relative JSON Pointer', + regex: 'regular expression' +} + +class AjvError extends Error { + // https://github.com/ajv-validator/ajv#validation-_errors + constructor (ajvErr, opts = {}) { + super() + + this._opts = { + ...defaultOpts, + ...opts + } + + this.pointer = ajvErr.dataPath + this.path = util.jsonPath(ajvErr.dataPath) + + const message = this._message(ajvErr) + + // TODO we need a better way of indicating that an error should be filtered out (e.g. + // sub-_errors for 'propertyNames' validations on object properties) + if (message === null) { + this.redundant = true + return + } + + this.message = util.capitalize(message) + '.' + + if (this._opts.includeOriginalError) this.original = ajvErr + if (this._opts.includeData) this.data = ajvErr.data + } + + toJSON () { + const v = { + path: this.path, + pointer: this.pointer, + message: this.message + } + + if (this._opts.includeOriginalError) v.original = this.original + if (this._opts.includeData) v.data = this.data + + return v + } + + _message (ajvErr) { + if (ajvErr.parentSchema.errorMessage) { + return `${this._fieldPreamble(ajvErr)} ${ajvErr.parentSchema.errorMessage}` + } + + const messageBuilder = this[`_buildMessage_${ajvErr.keyword}`] + if (typeof messageBuilder === 'function') { + return messageBuilder.bind(this)(ajvErr) + } + + // Default message + return `${this._fieldPreamble(ajvErr)} ${ajvErr.message}` + } + + _fieldPreamble (ajvErr) { + switch (this._opts.fieldLabels) { + case 'js': + if (ajvErr.dataPath === '') return 'the root value' + return `the value at ${util.jsonPath(ajvErr.dataPath).replace(/^\$/, '')}` + + case 'jsonPath': + return `the value at ${util.jsonPath(ajvErr.dataPath)}` + + case 'jsonPointer': + if (ajvErr.dataPath === '') return 'the root value' + return `the value at ${ajvErr.dataPath}` + + case 'title': + if (ajvErr.parentSchema.title) return ajvErr.parentSchema.title + if (ajvErr.dataPath === '') return 'the root value' + return `the value at ${ajvErr.dataPath}` + + default: + throw new Error(`invalid fieldLabels value: ${this._opts.fieldLabels}`) + } + } + + /* eslint-disable camelcase */ + + // -- base keywords + + _buildMessage_enum (err) { + const allowed = util.humanizeList(err.params.allowedValues.map(JSON.stringify), 'or') + + return `${this._fieldPreamble(err)} should be one of: ${allowed}` + } + + _buildMessage_type (err) { + const expectType = util.humanizeList(err.params.type.split(','), 'or') + const gotType = util.humanizeTypeOf(err.data) + + return `${this._fieldPreamble(err)} should be ${util.indefiniteArticle(expectType)} ${expectType} but it was ${gotType}` + } + + // -- strings + + _buildMessage_minLength (err) { + const limit = err.params.limit + const charsLimit = util.pluralize('character', limit) + const actual = err.data.length + const charsActual = util.pluralize('character', actual) + + return `${this._fieldPreamble(err)} should be ${limit} ${charsLimit} or more but it was ${actual} ${charsActual}` + } + + _buildMessage_maxLength (err) { + const limit = err.params.limit + const charsLimit = util.pluralize('character', limit) + const actual = err.data.length + const charsActual = util.pluralize('character', actual) + + return `${this._fieldPreamble(err)} should be ${limit} ${charsLimit} or fewer but it was ${actual} ${charsActual}` + } + + _buildMessage_pattern (err) { + if (err.schemaPath.endsWith('propertyNames/pattern')) return null + + const patternLabel = err.parentSchema.patternLabel + + if (patternLabel) { + return `${this._fieldPreamble(err)} should be ${patternLabel} but it was not` + } else { + return `${this._fieldPreamble(err)} is an invalid string` + } + } + + _buildMessage_format (err) { + const label = formatLabels[err.params.format] || err.params.format + + return `${this._fieldPreamble(err)} should be a valid ${label} string but it was not` + } + + // -- numbers + + _buildMessage_multipleOf (err) { + return `${this._fieldPreamble(err)} should be a multiple of ${err.params.multipleOf}` + } + + _buildMessage_minimum (err) { + return `${this._fieldPreamble(err)} should be equal to or greater than ${err.params.limit}` + } + + _buildMessage_exclusiveMinimum (err) { + return `${this._fieldPreamble(err)} should be greater than ${err.params.limit}` + } + + _buildMessage_maximum (err) { + return `${this._fieldPreamble(err)} should be equal to or less than ${err.params.limit}` + } + + _buildMessage_exclusiveMaximum (err) { + return `${this._fieldPreamble(err)} should be less than ${err.params.limit}` + } + + // -- objects + + _buildMessage_additionalProperties (err) { + const allowed = Object.keys(err.parentSchema.properties).join(', ') + const found = err.params.additionalProperty + + return `${this._fieldPreamble(err)} has an unexpected property, ${found}, which is not in the list of allowed properties (${allowed})` + } + + _buildMessage_required (err) { + const missingField = err.params.missingProperty + + return `${this._fieldPreamble(err)} is missing the required field '${missingField}'` + } + + _buildMessage_propertyNames (err) { + return `${this._fieldPreamble(err)} has an invalid property name ${JSON.stringify(err.params.propertyName)}` + } + + _buildMessage_minProperties (err) { + const expected = err.params.limit + const actual = Object.keys(err.data).length + return `${this._fieldPreamble(err)} should have ${expected} or more properties but it has ${actual}` + } + + _buildMessage_maxProperties (err) { + const expected = err.params.limit + const actual = Object.keys(err.data).length + return `${this._fieldPreamble(err)} should have ${expected} or fewer properties but it has ${actual}` + } + + _buildMessage_dependencies (err) { + const prop = err.params.property + const missing = err.params.missingProperty + + return `${this._fieldPreamble(err)} should have property ${missing} when ${prop} is present` + } + + // -- arrays + + _buildMessage_minItems (err) { + const min = err.params.limit + const actual = err.data.length + return `${this._fieldPreamble(err)} should have ${min} or more items but it has ${actual}` + } + + _buildMessage_maxItems (err) { + const max = err.params.limit + const actual = err.data.length + return `${this._fieldPreamble(err)} should have ${max} or fewer items but it has ${actual}` + } + + _buildMessage_uniqueItems (err) { + const { i, j } = err.params + return `${this._fieldPreamble(err)} should be unique but elements ${j} and ${i} are the same` + } +} + +module.exports.AjvError = AjvError + +class AggregateAjvError extends Error { + constructor (ajvErrors, opts = {}) { + super() + + this.name = 'AggregateAjvError' + + this._errors = ajvErrors + .map(e => new AjvError(e, opts)) + .filter(e => !e.redundant) + + this.message = this._errors.map(e => e.message).join(' ') + } + + toJSON () { + return this._errors.map(e => e.toJSON()) + } + + * [Symbol.iterator] () { + for (const err of this._errors) { + yield err + } + } +} + +module.exports.AggregateAjvError = AggregateAjvError diff --git a/packages/ajv-human-errors/index.test.js b/packages/ajv-human-errors/index.test.js new file mode 100644 index 0000000000..ca35225ca4 --- /dev/null +++ b/packages/ajv-human-errors/index.test.js @@ -0,0 +1,489 @@ +const { AggregateAjvError, AjvError } = require('./index') + +const Ajv = require('ajv') +const ajv = new Ajv({ + allErrors: true, + verbose: true, + jsonPointers: true +}) + +describe('AjvError', () => { + function error (schema, payload, opts = {}) { + ajv.validate(schema, payload) + if (!ajv.errors) throw new Error("ajv didn't return any errors") + return new AjvError(ajv.errors[ajv.errors.length - 1], opts) + } + + describe('base keywords', () => { + it('returns type errors', () => { + expect(error( + { type: 'object' }, + [] + ).message).toStrictEqual( + 'The root value should be an object but it was an array.' + ) + + expect(error( + { type: 'object' }, + 1 + ).message).toStrictEqual( + 'The root value should be an object but it was a number.' + ) + + expect(error( + { type: 'number' }, + 'oops' + ).message).toStrictEqual( + 'The root value should be a number but it was a string.' + ) + + expect(error( + { type: ['number', 'string'] }, + {} + ).message).toStrictEqual( + 'The root value should be a number or string but it was an object.' + ) + }) + + it('returns nested errors', () => { + expect(error( + { properties: { foo: { properties: { bar: { type: 'string' } } } } }, + { foo: { bar: {} } } + ).message).toStrictEqual( + 'The value at /foo/bar should be a string but it was an object.' + ) + }) + + it('returns enum errors', () => { + expect(error( + { enum: ['foo', 'bar', 10] }, + {} + ).message).toStrictEqual( + 'The root value should be one of: "foo", "bar", or 10.' + ) + }) + }) + + describe('options', () => { + describe.each([ + ['js', 'The value at .foo should be a string but it was a number.'], + ['jsonPath', 'The value at $.foo should be a string but it was a number.'], + ['jsonPointer', 'The value at /foo should be a string but it was a number.'], + ['title', 'Smurf should be a string but it was a number.'] + ])("fieldLabels: '%s'", (fieldLabels, expected) => { + expect(error( + { type: 'object', properties: { foo: { title: 'Smurf', type: 'string' } } }, + { foo: 1 }, + { fieldLabels: fieldLabels } + ).message).toStrictEqual(expected) + }) + + it('handles includeOriginalError', () => { + expect(error( + { type: 'string' }, + {}, + {} + ).original).toBeUndefined() + + expect(error( + { type: 'string' }, + {}, + { includeOriginalError: true } + ).original).toStrictEqual({ + data: {}, + dataPath: '', + keyword: 'type', + message: 'should be string', + params: { type: 'string' }, + parentSchema: { type: 'string' }, + schema: 'string', + schemaPath: '#/type' + }) + }) + + it('handles includeData', () => { + expect(error( + { type: 'string' }, + { oops: true }, + {} + ).data).toBeUndefined() + + expect(error( + { type: 'string' }, + { oops: true }, + { includeData: true } + ).data).toStrictEqual( + { oops: true } + ) + }) + }) + + describe('strings', () => { + it('returns format errors', () => { + expect(error( + { type: 'string', minLength: 10 }, + 'oops' + ).message).toStrictEqual( + 'The root value should be 10 characters or more but it was 4 characters.' + ) + + expect(error( + { type: 'string', minLength: 1 }, + '' + ).message).toStrictEqual( + 'The root value should be 1 character or more but it was 0 characters.' + ) + + expect(error( + { type: 'string', maxLength: 3 }, + 'oops' + ).message).toStrictEqual( + 'The root value should be 3 characters or fewer but it was 4 characters.' + ) + + expect(error( + { type: 'string', pattern: '^\\d+$' }, + 'oops' + ).message).toStrictEqual( + 'The root value is an invalid string.' + ) + + expect(error( + { type: 'string', pattern: '^\\d+$', patternLabel: 'an integer string' }, + 'oops' + ).message).toStrictEqual( + 'The root value should be an integer string but it was not.' + ) + + const testcases = [ + { format: 'date-time', label: 'date and time', value: '' }, + { format: 'time', label: 'time', value: '' }, + { format: 'date', label: 'date', value: '' }, + { format: 'email', label: 'email address', value: '' }, + { format: 'hostname', label: 'hostname', value: '' }, + { format: 'ipv4', label: 'IPv4 address', value: '' }, + { format: 'ipv6', label: 'IPv6 address', value: '' }, + { format: 'uri', label: 'URI', value: '' }, + { format: 'regex', label: 'regular expression', value: '[' } + ] + + testcases.forEach(testcase => { + const { format, label, value } = testcase + expect(error( + { type: 'string', format }, + value + ).message).toStrictEqual( + `The root value should be a valid ${label} string but it was not.` + ) + }) + }) + }) + + describe('numbers', () => { + it('returns multipleOf errors', () => { + expect(error( + { type: 'number', multipleOf: 10 }, + 1 + ).message).toStrictEqual( + 'The root value should be a multiple of 10.' + ) + }) + + it('returns range errors', () => { + expect(error( + { type: 'number', minimum: 5 }, + 1 + ).message).toStrictEqual( + 'The root value should be equal to or greater than 5.' + ) + + expect(error( + { type: 'number', exclusiveMinimum: 5 }, + 5 + ).message).toStrictEqual( + 'The root value should be greater than 5.' + ) + + expect(error( + { type: 'number', maximum: 5 }, + 10 + ).message).toStrictEqual( + 'The root value should be equal to or less than 5.' + ) + + expect(error( + { type: 'number', exclusiveMaximum: 5 }, + 5 + ).message).toStrictEqual( + 'The root value should be less than 5.' + ) + }) + }) + + describe('objects', () => { + it('returns additionalProperty errors', () => { + expect(error( + { properties: { a: {}, d: {} }, additionalProperties: false }, + { a: 1, b: 2, c: 3 } + ).message).toStrictEqual( + 'The root value has an unexpected property, c, which is not in the list of allowed properties (a, d).' + ) + + expect(error( + { properties: { a: {} }, additionalProperties: { type: 'string' } }, + { a: 1, b: 2 } + ).message).toStrictEqual( + 'The value at /b should be a string but it was a number.' + ) + }) + + it('returns required errors', () => { + expect(error( + { required: ['foo'] }, + {} + ).message).toStrictEqual( + "The root value is missing the required field 'foo'." + ) + + expect(error( + { required: ['foo', 'bar'] }, + {} + ).message).toStrictEqual( + "The root value is missing the required field 'bar'." + ) + }) + + it('returns propertyNames errors', () => { + expect(error( + { type: 'object', propertyNames: { pattern: '^\\d+$' } }, + { oops: 1 } + ).message).toStrictEqual( + 'The root value has an invalid property name "oops".' + ) + }) + + it('returns size errors', () => { + expect(error( + { type: 'object', minProperties: 5 }, + { a: 1 } + ).message).toStrictEqual( + 'The root value should have 5 or more properties but it has 1.' + ) + + expect(error( + { type: 'object', maxProperties: 2 }, + { a: 1, b: 2, c: 3 } + ).message).toStrictEqual( + 'The root value should have 2 or fewer properties but it has 3.' + ) + }) + + it('returns dependency errors', () => { + expect(error( + { type: 'object', dependencies: { a: ['b', 'c'] } }, + { a: 1 } + ).message).toStrictEqual( + 'The root value should have property c when a is present.' + ) + }) + }) + + describe('arrays', () => { + it('returns items errors', () => { + expect(error( + { type: 'array', items: { type: 'number' } }, + ['x'] + ).message).toStrictEqual( + 'The value at /0 should be a number but it was a string.' + ) + + expect(error( + { properties: { nums: { type: 'array', items: { type: 'number' } } } }, + { nums: [0, 'x'] } + ).message).toStrictEqual( + 'The value at /nums/1 should be a number but it was a string.' + ) + + expect(error( + { properties: { nums: { type: 'array', items: { enum: ['a'] } } } }, + { nums: [0, 'x'] } + ).message).toStrictEqual( + 'The value at /nums/1 should be one of: "a".' + ) + + expect(error( + { properties: { tuple: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] } } }, + { tuple: [0, 'x'] } + ).message).toStrictEqual( + 'The value at /tuple/1 should be a number but it was a string.' + ) + }) + + it('returns length errors', () => { + expect(error( + { type: 'array', minItems: 1 }, + [] + ).message).toStrictEqual( + 'The root value should have 1 or more items but it has 0.' + ) + + expect(error( + { type: 'array', maxItems: 1 }, + [0, 1, 2] + ).message).toStrictEqual( + 'The root value should have 1 or fewer items but it has 3.' + ) + }) + + it('returns uniqueItems errors', () => { + expect(error( + { type: 'array', uniqueItems: true }, + [0, 1, 2, 0, 1] + ).message).toStrictEqual( + 'The root value should be unique but elements 1 and 4 are the same.' + ) + }) + }) + + describe('toJSON', () => { + it('returns object', () => { + expect(error( + { + type: 'object', + properties: { + arr: { type: 'array', uniqueItems: true } + } + }, + { arr: [0, 1, 2, 0, 1] } + ).toJSON()).toStrictEqual({ + message: 'The value at /arr should be unique but elements 1 and 4 are the same.', + path: '$.arr', + pointer: '/arr' + }) + + expect(error( + { + type: 'object', + properties: { + arr: { type: 'array', uniqueItems: true } + } + }, + { arr: [0, 1, 2, 0, 1] }, + { includeOriginalError: true, includeData: true } + ).toJSON()).toStrictEqual({ + data: [0, 1, 2, 0, 1], + message: 'The value at /arr should be unique but elements 1 and 4 are the same.', + original: { + data: [0, 1, 2, 0, 1], + dataPath: '/arr', + keyword: 'uniqueItems', + message: 'should NOT have duplicate items (items ## 1 and 4 are identical)', + params: { i: 4, j: 1 }, + parentSchema: { type: 'array', uniqueItems: true }, + schema: true, + schemaPath: '#/properties/arr/uniqueItems' + }, + path: '$.arr', + pointer: '/arr' + }) + }) + }) + + describe('schema options', () => { + describe('errorMessage', () => { + it('overrides generated error message', () => { + expect(error( + { type: 'string', errorMessage: 'should be a fancy string' }, + {} + ).message).toStrictEqual( + 'The root value should be a fancy string.' + ) + + expect(error( + { type: 'string', errorMessage: 'should be a fancy string' }, + {}, + { fieldLabels: 'jsonPath' } + ).message).toStrictEqual( + 'The value at $ should be a fancy string.' + ) + }) + }) + }) +}) + +describe('AggregateAjvError', () => { + function error (schema, payload, opts = {}) { + ajv.validate(schema, payload) + if (!ajv.errors) throw new Error("ajv didn't return any errors") + return new AggregateAjvError(ajv.errors, opts) + } + + it('aggregates errors', () => { + expect(error( + { + type: 'object', + properties: { + a: { type: 'string' }, + b: { type: 'number' } + } + }, + { a: null, b: null } + ).message).toStrictEqual( + 'The value at /a should be a string but it was null. The value at /b should be a number but it was null.' + ) + }) + + it('accepts AjvError options', () => { + expect(error( + { + type: 'object', + properties: { + a: { title: 'Stringy', type: 'string' }, + b: { type: 'number' } + } + }, + { a: null, b: null }, + { fieldLabels: 'title' } + ).message).toStrictEqual( + 'Stringy should be a string but it was null. The value at /b should be a number but it was null.' + ) + }) + + it('supports JSON', () => { + expect(error( + { + type: 'object', + properties: { + arr: { type: 'array', uniqueItems: true } + } + }, + { arr: [0, 1, 2, 0, 1] } + ).toJSON()).toStrictEqual([ + { + message: 'The value at /arr should be unique but elements 1 and 4 are the same.', + path: '$.arr', + pointer: '/arr' + } + ]) + }) + + it('is an iterator', () => { + const err = error( + { + type: 'object', + properties: { + a: { type: 'string' }, + b: { type: 'number' } + } + }, + { a: null, b: null } + ) + const errors = [...err] + + expect(errors.length).toStrictEqual(2) + expect(errors.map(e => e.message)).toStrictEqual([ + 'The value at /a should be a string but it was null.', + 'The value at /b should be a number but it was null.' + ]) + }) +}) diff --git a/packages/ajv-human-errors/package.json b/packages/ajv-human-errors/package.json new file mode 100644 index 0000000000..48ffa6768c --- /dev/null +++ b/packages/ajv-human-errors/package.json @@ -0,0 +1,19 @@ +{ + "name": "@segment/ajv-human-errors", + "version": "1.1.0", + "description": "Human-readable error messages for Ajv (Another JSON Schema Validator).", + "repository": { + "type": "git", + "url": "https://github.com/segmentio/action-destinations", + "directory": "packages/ajv-human-errors" + }, + "main": "index.js", + "scripts": { + "test": "jest" + }, + "license": "UNLICENSED", + "devDependencies": { + "ajv": "^6.12.3" + }, + "dependencies": {} +} diff --git a/packages/ajv-human-errors/util.js b/packages/ajv-human-errors/util.js new file mode 100644 index 0000000000..285ba6e1d4 --- /dev/null +++ b/packages/ajv-human-errors/util.js @@ -0,0 +1,80 @@ +module.exports.capitalize = (s) => { + if (typeof s !== 'string' || s.length === 0) return s + return s.charAt(0).toUpperCase() + s.slice(1) +} + +// Ultra-naieve implementation because we only pluralize "character" currently. +module.exports.pluralize = (s, num) => { + if (num === 1) return s + else return s + 's' +} + +// jsonPath converts a JSON Pointer to JSON Path. +function jsonPath (s) { + if (s === '') return '$' + + const path = s + .substring(1) + .split(/\//) + .map(s => { + // unescape JSON Pointer and escape JSON Path + return s.replace(/~1/g, '/').replace(/~0/g, '~').replace(/\./g, '\\.') + }) + .map(s => { + // use array access format + if (s.match(/^\d+$/)) return `[${s}]` + else return '.' + s + }).join('') + + return '$' + path +} + +module.exports.jsonPath = jsonPath + +// humanizePath takes a JSON Pointer string and converts it to a more human-readable form using JSON +// Path. +module.exports.humanizePath = (s) => { + if (s === '') return 'the root value' + return `the value at ${jsonPath(s)}` +} + +// humanizeTypeOf returns a human-readable type for the given value with an indefinite article if it +// makes sense. +module.exports.humanizeTypeOf = (v) => { + const raw = typeof v + switch (raw) { + case 'object': + if (v === null) return 'null' + if (Array.isArray(v)) return 'an array' + return 'an object' + case 'undefined': + return 'undefined' + default: + return `${indefiniteArticle(raw)} ${raw}` + } +} + +// indefiniteArticle returns "a" or "an" for the given word. Obviously this is not comprehensive, +// but it covers the possible return values of `typeof`. +function indefiniteArticle (s) { + switch (s[0]) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return 'an' + default: + return 'a' + } +} + +module.exports.indefiniteArticle = indefiniteArticle + +// humanizeList returns a human-readable version of the given list of values. +module.exports.humanizeList = (arr, conjunction = 'and') => { + if (arr.length === 0) return 'nothing' + if (arr.length === 1) return arr[0] + if (arr.length === 2) return `${arr[0]} ${conjunction} ${arr[1]}` + return `${arr.slice(0, -1).join(', ')}, ${conjunction} ${arr[arr.length - 1]}` +} diff --git a/packages/ajv-human-errors/util.test.js b/packages/ajv-human-errors/util.test.js new file mode 100644 index 0000000000..1396ffb1e1 --- /dev/null +++ b/packages/ajv-human-errors/util.test.js @@ -0,0 +1,66 @@ +const util = require('./util') + +describe('capitalize', () => { + it('returns a capitalized string', () => { + expect(util.capitalize(null)).toStrictEqual(null) + expect(util.capitalize({})).toStrictEqual({}) + expect(util.capitalize('')).toStrictEqual('') + expect(util.capitalize('a')).toStrictEqual('A') + expect(util.capitalize('foo bar')).toStrictEqual('Foo bar') + }) +}) + +describe('jsonPath', () => { + it('translates JSON Pointer to JSON Path', () => { + expect(util.jsonPath('')).toStrictEqual('$') + expect(util.jsonPath('/foo')).toStrictEqual('$.foo') + expect(util.jsonPath('/foo/bar')).toStrictEqual('$.foo.bar') + expect(util.jsonPath('/foo/bar.baz')).toStrictEqual('$.foo.bar\\.baz') + expect(util.jsonPath('/foo/bar/0')).toStrictEqual('$.foo.bar[0]') + expect(util.jsonPath('/foo/bar/0/baz~0~1/x')).toStrictEqual('$.foo.bar[0].baz~/.x') + }) +}) + +describe('humanizePath', () => { + it('returns humanize path string', () => { + expect(util.humanizePath('')).toStrictEqual('the root value') + expect(util.humanizePath('/foo/bar')).toStrictEqual('the value at $.foo.bar') + }) +}) + +describe('humanizeTypeOf', () => { + it('returns human-readable English', () => { + expect(util.humanizeTypeOf(null)).toStrictEqual('null') + expect(util.humanizeTypeOf(undefined)).toStrictEqual('undefined') + expect(util.humanizeTypeOf({})).toStrictEqual('an object') + expect(util.humanizeTypeOf([])).toStrictEqual('an array') + expect(util.humanizeTypeOf(1)).toStrictEqual('a number') + expect(util.humanizeTypeOf('x')).toStrictEqual('a string') + }) +}) + +describe('indefiniteArticle', () => { + it('returns the right indefinite article for basic JS types', () => { + expect(util.indefiniteArticle('array')).toStrictEqual('an') + expect(util.indefiniteArticle('object')).toStrictEqual('an') + expect(util.indefiniteArticle('string')).toStrictEqual('a') + expect(util.indefiniteArticle('number')).toStrictEqual('a') + }) +}) + +describe('humanizeList', () => { + it('returns human-readable English', () => { + expect(util.humanizeList([])).toStrictEqual('nothing') + expect(util.humanizeList(['a'])).toStrictEqual('a') + expect(util.humanizeList(['a', 'b'], 'or')).toStrictEqual('a or b') + expect(util.humanizeList(['a', 'b', 'c'], 'and')).toStrictEqual('a, b, and c') + }) +}) + +describe('pluralize', () => { + it('returns basic pluralized form', () => { + expect(util.pluralize('character', 0)).toStrictEqual('characters') + expect(util.pluralize('character', 1)).toStrictEqual('character') + expect(util.pluralize('character', 2)).toStrictEqual('characters') + }) +}) diff --git a/packages/cli/bin/run b/packages/cli/bin/run new file mode 100755 index 0000000000..d8fe3365f5 --- /dev/null +++ b/packages/cli/bin/run @@ -0,0 +1,24 @@ +#!/usr/bin/env node + +const path = require('path') +const fs = require('fs') +const hasDist = fs.existsSync(path.resolve(__dirname, '../dist')) + +if (hasDist) { + // skip ts-node when we have a compiled CLI + process.env.OCLIF_TS_NODE = 0 +} else { + const { register } = require('tsconfig-paths') + const tsConfig = require('../tsconfig.json') + + // For now we have to register tsconfig-paths manually + register({ + baseUrl: path.join(__dirname, '../'), + paths: tsConfig.compilerOptions.paths + }) +} + +require('@oclif/command') + .run() + .then(require('@oclif/command/flush')) + .catch(require('@oclif/errors/handle')) \ No newline at end of file diff --git a/packages/cli/bin/run.cmd b/packages/cli/bin/run.cmd new file mode 100644 index 0000000000..968fc30758 --- /dev/null +++ b/packages/cli/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000000..fa5ecf33c4 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,100 @@ +{ + "name": "@segment/actions-cli", + "description": "CLI to interact with Segment integrations", + "version": "2.4.2", + "repository": { + "type": "git", + "url": "https://github.com/segmentio/action-destinations", + "directory": "packages/cli" + }, + "engineStrict": true, + "bin": { + "segment": "./bin/run" + }, + "files": [ + "/bin", + "/dist", + "/oclif.manifest.json" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "private": true, + "scripts": { + "postpack": "rm -f oclif.manifest.json", + "prepack": "yarn build && oclif-dev manifest && oclif-dev readme", + "build": "yarn clean && yarn tsc -b tsconfig.build.json", + "clean": "tsc -b tsconfig.build.json --clean", + "postclean": "rm -rf dist", + "create:destination": "npx segment init", + "test": "jest", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@oclif/dev-cli": "^1", + "@oclif/test": "^1.2.8", + "@types/fs-extra": "^9.0.11", + "@types/jest": "^26.0.23", + "@types/jscodeshift": "^0.11.0", + "@types/json-diff": "^0.5.1", + "@types/json-schema": "^7.0.7", + "@types/lodash": "^4.14.168", + "@types/mustache": "^4.1.0", + "@types/node": "^10.17.0", + "@types/prompts": "^2.0.10", + "@types/rimraf": "^3.0.0", + "@types/to-title-case": "^1.0.0", + "chai": "^4.3.4", + "jest": "26.6.3", + "rimraf": "^3.0.2" + }, + "dependencies": { + "@oclif/command": "^1", + "@oclif/config": "^1", + "@oclif/errors": "^1", + "@oclif/plugin-help": "^3", + "@segment/actions-core": "^2.2.1", + "@segment/destination-actions": "^2.4.2", + "chalk": "^4.1.1", + "chokidar": "^3.5.1", + "fs-extra": "^9.1.0", + "globby": "^11.0.3", + "jscodeshift": "^0.12.0", + "jscodeshift-add-imports": "^1.0.10", + "json-diff": "^0.5.4", + "json-schema-to-typescript": "^10.1.4", + "lodash": "^4.17.20", + "mustache": "^4.2.0", + "ora": "^5.4.0", + "prompts": "^2.4.1", + "slugify": "^1.5.0", + "to-title-case": "^1.0.0", + "tsconfig-paths": "^3.9.0", + "tslib": "^2" + }, + "optionalDependencies": { + "@segment/control-plane-service-client": "git+ssh://git@github.com/segmentio/control-plane-service-js-client.git#master" + }, + "oclif": { + "commands": "./dist/commands", + "bin": "segment", + "plugins": [ + "@oclif/plugin-help" + ], + "topics": { + "generate": { + "description": "scaffold resources or types for an integration" + } + } + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "testRegex": "((\\.|/)(test))\\.(tsx?|json)$", + "modulePathIgnorePatterns": [ + "/dist/" + ], + "moduleNameMapper": { + "src/lib/(.*)": "/src/lib/$1" + } + } +} diff --git a/packages/cli/src/__tests__/init.test.ts b/packages/cli/src/__tests__/init.test.ts new file mode 100644 index 0000000000..37d14b3204 --- /dev/null +++ b/packages/cli/src/__tests__/init.test.ts @@ -0,0 +1,70 @@ +import { test } from '@oclif/test' +import * as fs from 'fs' +import * as path from 'path' +import * as prompt from '../lib/prompt' +import * as rimraf from 'rimraf' + +describe('cli init command', () => { + const testDir = path.join('.', 'testResults') + beforeAll(() => { + if (!fs.existsSync(testDir)) { + fs.mkdir(testDir, (err) => { + console.log(err) + }) + } + }) + + afterAll(() => { + if (fs.existsSync(testDir)) { + rimraf.sync(testDir) + } + }) + + test + .stub(prompt, 'autoPrompt', () => { + return { directory: testDir, name: 'test basic', slug: 'test-basic', template: 'basic-auth' } + }) + .stdout() + .command(['init']) + .it('should scaffold an action with basic auth scheme', (ctx) => { + expect(ctx.stdout).toContain('Done creating "test basic"') + const scaffoldedAction = fs.readFileSync(path.join(testDir, 'test-basic', 'index.ts'), 'utf8') + expect(scaffoldedAction).toContain("scheme: 'basic'") + }) + + test + .stub(prompt, 'autoPrompt', () => { + return { directory: testDir, name: 'test custom auth', slug: 'test-custom-auth', template: 'custom-auth' } + }) + .stdout() + .command(['init']) + .it('should scaffold an action with custom auth scheme', (ctx) => { + expect(ctx.stdout).toContain('Done creating "test custom auth"') + const scaffoldedAction = fs.readFileSync(path.join(testDir, 'test-custom-auth', 'index.ts'), 'utf8') + expect(scaffoldedAction).toContain("scheme: 'custom'") + }) + + test + .stub(prompt, 'autoPrompt', () => { + return { directory: testDir, name: 'test minimal', slug: 'test-minimal', template: 'minimal' } + }) + .stdout() + .command(['init']) + .it('should scaffold a minimal action', (ctx) => { + expect(ctx.stdout).toContain('Done creating "test minimal"') + const scaffoldedAction = fs.readFileSync(path.join(testDir, 'test-minimal', 'index.ts'), 'utf8') + expect(scaffoldedAction).toContain("name: 'test minimal'") + }) + + test + .stub(prompt, 'autoPrompt', () => { + return { directory: testDir, name: 'test oauth', slug: 'test-oauth', template: 'oauth2-auth' } + }) + .stdout() + .command(['init']) + .it('should scaffold a oauth2 action', (ctx) => { + expect(ctx.stdout).toContain('Done creating "test oauth"') + const scaffoldedAction = fs.readFileSync(path.join(testDir, 'test-oauth', 'index.ts'), 'utf8') + expect(scaffoldedAction).toContain("scheme: 'oauth2'") + }) +}) diff --git a/packages/cli/src/commands/generate/action.ts b/packages/cli/src/commands/generate/action.ts new file mode 100644 index 0000000000..918076c553 --- /dev/null +++ b/packages/cli/src/commands/generate/action.ts @@ -0,0 +1,147 @@ +import { Command, flags } from '@oclif/command' +import chalk from 'chalk' +import fs from 'fs-extra' +import globby from 'globby' +import { camelCase } from 'lodash' +import toTitleCase from 'to-title-case' +import ora from 'ora' +import path from 'path' +import { autoPrompt } from '../../lib/prompt' +import { renderTemplates } from '../../lib/templates' +import { addKeyToDefaultExport } from '../../lib/codemods' +import GenerateTypes from './types' + +export default class GenerateAction extends Command { + private spinner: ora.Ora = ora() + + static description = `Scaffolds a new integration action.` + + static examples = [ + `$ segment generate:action ACTION `, + `$ segment generate:action postToChannel server --directory=./destinations/slack` + ] + + static flags = { + help: flags.help({ char: 'h' }), + force: flags.boolean({ char: 'f' }), + title: flags.string({ char: 't', description: 'the display name of the action' }), + directory: flags.string({ char: 'd', description: 'base directory to scaffold the action' }) + } + + static args = [ + { name: 'name', description: 'the action name', required: true }, + { name: 'type', description: 'the type of action (browser, server)', required: true, default: 'server' } + ] + + async integrationDirs(glob: string) { + const integrationDirs = await globby(glob, { + expandDirectories: false, + onlyDirectories: true, + gitignore: true, + ignore: ['node_modules'] + }) + + return integrationDirs + } + + parseArgs() { + return this.parse(GenerateAction) + } + + async run() { + const { args, flags } = this.parseArgs() + // eslint-disable-next-line prefer-const + let integrationsGlob = './packages/destination-actions/src/destinations/*' + // if (args.type === 'browser') { + // integrationsGlob = './packages/browser-destinations/src/destinations/*' + // } + const integrationDirs = await this.integrationDirs(integrationsGlob) + + const answers = await autoPrompt(flags, [ + { + type: 'text', + name: 'title', + message: 'Action title:', + initial: toTitleCase(args.name), + format: (val) => toTitleCase(val) + }, + { + type: 'select', + name: 'directory', + message: 'Which integration (directory)?', + choices: integrationDirs.map((integrationPath) => { + const [name] = integrationPath.split(path.sep).reverse() + return { + title: name, + value: integrationPath + } + }) + } + ]) + + const slug = camelCase(args.name) + const directory = answers.directory || './' + const relativePath = path.join(directory, slug) + const targetDirectory = path.join(process.cwd(), relativePath) + + let templatePath = path.join(__dirname, '../../../templates/actions/empty-action') + if (args.type === 'browser') { + templatePath = path.join(__dirname, '../../../templates/actions/empty-browser-action') + } + + try { + this.spinner.start(`Creating ${chalk.bold(args.name)}`) + renderTemplates( + templatePath, + targetDirectory, + { + name: answers.title, + description: '', + slug + }, + flags.force + ) + this.spinner.succeed(`Scaffold action`) + } catch (err) { + this.spinner.fail(`Scaffold action: ${chalk.red(err.message)}`) + this.exit() + } + + // Update destination with action + const entryFile = require.resolve(path.relative(__dirname, path.join(process.cwd(), directory))) + try { + this.spinner.start(chalk`Updating destination definition`) + const destinationStr = fs.readFileSync(entryFile, 'utf8') + const updatedCode = addKeyToDefaultExport(destinationStr, 'actions', slug) + fs.writeFileSync(entryFile, updatedCode, 'utf8') + this.spinner.succeed() + } catch (err) { + // we can't update browser destination default exports normally due + // to the default export being a function call + + if (args.type === 'server') { + this.spinner.fail(chalk`Failed to update your destination imports: ${err.message}`) + this.exit() + } + } + + try { + this.spinner.start(chalk`Generating types for {magenta ${slug}} action`) + await GenerateTypes.run(['--path', entryFile]) + this.spinner.succeed() + } catch (err) { + this.spinner.fail(chalk`Generating types for {magenta ${slug}} action: ${err.message}`) + this.exit() + } + + this.log(chalk.green(`Done creating "${args.name}" 🎉`)) + this.log(chalk.green(`Start coding! cd ${targetDirectory}`)) + } + + async catch(error: unknown) { + if (this.spinner?.isSpinning) { + this.spinner.fail() + } + throw error + } +} diff --git a/packages/cli/src/commands/generate/types.ts b/packages/cli/src/commands/generate/types.ts new file mode 100644 index 0000000000..d0a93cb364 --- /dev/null +++ b/packages/cli/src/commands/generate/types.ts @@ -0,0 +1,156 @@ +import { Command, flags } from '@oclif/command' +import { fieldsToJsonSchema, InputField } from '@segment/actions-core' +import chokidar from 'chokidar' +import fs from 'fs-extra' +import globby from 'globby' +import { JSONSchema4 } from 'json-schema' +import { compile } from 'json-schema-to-typescript' +import path from 'path' +import prettier from 'prettier' +import { loadDestination } from '../../lib/destinations' +import { OAUTH_GENERATE_TYPES, OAUTH_SCHEME, RESERVED_FIELD_NAMES } from '../../constants' + +const pretterOptions = prettier.resolveConfig.sync(process.cwd()) + +export default class GenerateTypes extends Command { + static description = `Generates TypeScript definitions for an integration.` + + static examples = [ + `$ segment generate:types`, + `$ segment generate:types --path ./packages/*/src/destinations/*/index.ts` + ] + + // Allow variable length args (to work with tools like lint-staged) + static strict = false + + static flags = { + help: flags.help({ char: 'h' }), + path: flags.string({ + char: 'p', + description: 'file path for the integration(s). Accepts glob patterns.', + multiple: true + }), + watch: flags.boolean({ char: 'w', description: 'Watch for file changes to regenerate types' }) + } + + static args = [] + + async run() { + const { flags } = this.parse(GenerateTypes) + + const globs = flags.path || ['./packages/*/src/destinations/*/index.ts'] + const files = await globby(globs, { + expandDirectories: false, + gitignore: true, + ignore: ['node_modules'] + }) + + for (const file of files) { + await this.handleFile(file) + } + + if (flags.watch) { + const dirsToWatch = files.map((file) => path.dirname(file)) + + const watcher = chokidar.watch(dirsToWatch, { + cwd: process.cwd(), + ignored: '**/*/generated-types.ts' + }) + + watcher.on('change', (filePath) => { + this.debug(`Regenerating types for ${filePath} ..`) + + // Find matching parent directory for the entrypoint file + const parentDir = dirsToWatch.find((parent) => { + const relative = path.relative(parent, filePath) + return relative && !relative.startsWith('..') && !path.isAbsolute(relative) + }) + if (!parentDir) { + return + } + + this.handleFile(parentDir).catch((error) => { + this.debug(`Error generating types for ${filePath}: ${error.message}`) + }) + }) + + watcher.on('error', (error) => { + this.error(`Error: ${error.message}`) + }) + + watcher.once('ready', () => { + this.log('Watching files for changes ..') + }) + } + } + + async handleFile(file: string): Promise { + const destination = await loadDestination(file).catch((error) => { + this.debug(`Couldn't load ${file}: ${error.message}`) + return null + }) + + if (!destination) { + return + } + + const stats = fs.statSync(file) + const parentDir = stats.isDirectory() ? file : path.dirname(file) + let authFields = destination.authentication?.fields + if (authFields) { + for (const key in authFields) { + if (RESERVED_FIELD_NAMES.includes(key.toLowerCase())) { + throw new Error(`Field definition in destination ${destination.name} is using a reserved name: ${key}`) + } + } + } + + if (destination.authentication?.scheme === OAUTH_SCHEME) { + authFields = Object.assign({ ...OAUTH_GENERATE_TYPES }, { ...authFields }) + } + const types = await generateTypes(authFields, 'Settings') + fs.writeFileSync(path.join(parentDir, './generated-types.ts'), types) + + // TODO how to load directory structure consistently? + for (const [slug, action] of Object.entries(destination.actions)) { + const types = await generateTypes(action.fields, 'Payload') + if (fs.pathExistsSync(path.join(parentDir, `${slug}`))) { + fs.writeFileSync(path.join(parentDir, slug, 'generated-types.ts'), types) + } else { + fs.writeFileSync(path.join(parentDir, `./${slug}.types.ts`), types) + } + } + } +} + +async function generateTypes(fields: Record = {}, name: string) { + const schema = prepareSchema(fields) + + return compile(schema, name, { + bannerComment: '// Generated file. DO NOT MODIFY IT BY HAND.', + style: pretterOptions ?? undefined + }) +} + +function prepareSchema(fields: Record): JSONSchema4 { + let schema = fieldsToJsonSchema(fields) + // Remove titles so it produces cleaner output + schema = removeTitles(schema) + return schema +} + +function removeTitles(schema: JSONSchema4) { + const copy = { ...schema } + + delete copy.title + + if (copy.type === 'object' && copy.properties) { + for (const [key, property] of Object.entries(copy.properties)) { + copy.properties[key] = removeTitles(property) + } + } else if (copy.type === 'array' && copy.items) { + copy.items = removeTitles(copy.items) + } + + return copy +} diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts new file mode 100644 index 0000000000..1d8002d8c2 --- /dev/null +++ b/packages/cli/src/commands/init.ts @@ -0,0 +1,142 @@ +import { Command, flags } from '@oclif/command' +import chalk from 'chalk' +import ora from 'ora' +import path from 'path' +import slugify from 'slugify' +import toTitleCase from 'to-title-case' +import { autoPrompt } from '../lib/prompt' +import { renderTemplates } from '../lib/templates' +import GenerateTypes from './generate/types' + +export default class Init extends Command { + private spinner: ora.Ora = ora() + + static description = `Scaffolds a new integration with a template. This does not register or deploy the integration.` + + static examples = [ + `$ segment init my-integration`, + `$ segment init my-integration --directory packages/destination-actions --template basic-auth` + ] + + static flags = { + help: flags.help({ char: 'h' }), + directory: flags.string({ + char: 'd', + description: 'target directory to scaffold the integration', + default: './packages/destination-actions/src/destinations' + }), + name: flags.string({ char: 'n', description: 'name of the integration' }), + slug: flags.string({ char: 's', description: 'url-friendly slug of the integration' }), + template: flags.enum({ + char: 't', + options: ['basic-auth', 'custom-auth', 'oauth2-auth', 'minimal'], + description: 'the template to use to scaffold your integration' + }) + } + + static args = [ + { + name: 'path', + description: 'path to scaffold the integration' + } + ] + + parseFlags() { + return this.parse(Init) + } + + async run() { + const { args, flags } = this.parseFlags() + const answers = await autoPrompt(flags, [ + { + type: 'text', + name: 'name', + message: 'Integration name:', + format: (val) => toTitleCase(val) + }, + { + type: 'text', + name: 'slug', + // @ts-ignore the types are wrong + initial: (prev) => slugify(flags.name || prev).toLowerCase(), + message: 'Integration slug:', + format: (val) => slugify(val).toLowerCase() + }, + { + type: 'select', + name: 'template', + message: 'What template do you want to use?', + choices: [ + { + title: 'Custom Auth', + description: 'Most "API Key" based integrations should use this.', + value: 'custom-auth' + }, + { + title: 'Browser Destination', + description: 'Creates an Analytics JS compatible Destination.', + value: 'browser' + }, + { + title: 'Basic Auth', + description: 'Integrations that use Basic Auth: https://tools.ietf.org/html/rfc7617', + value: 'basic-auth' + }, + { + title: 'OAuth2 Auth', + description: 'Use for APIs that support OAuth2.', + value: 'oauth2-auth' + }, + { + title: 'Minimal', + value: 'minimal' + } + ], + initial: 0 + } + ]) + + const { name, slug, template } = answers + if (!name || !slug || !template) { + this.exit() + } + + // eslint-disable-next-line prefer-const + let directory = answers.directory + // if (template === 'browser' && directory === Init.flags.directory.default) { + // directory = './packages/browser-destinations/src/destinations' + // } + + // For now, include the slug in the path, but when we support external repos, we'll have to change this + const relativePath = path.join(directory, args.path || slug) + const targetDirectory = path.join(process.cwd(), relativePath) + const templatePath = path.join(__dirname, '../../templates/destinations', template) + + try { + this.spinner.start(`Creating ${chalk.bold(name)}`) + renderTemplates(templatePath, targetDirectory, answers) + this.spinner.succeed(`Scaffold integration`) + } catch (err) { + this.spinner.fail(`Scaffold integration: ${chalk.red(err.message)}`) + this.exit() + } + + try { + this.spinner.start(chalk`Generating types for {magenta ${slug}} destination`) + await GenerateTypes.run(['--path', `${relativePath}/index.ts`]) + this.spinner.succeed() + } catch (err) { + this.spinner.fail(chalk`Generating types for {magenta ${slug}} destination: ${err.message}`) + } + + this.log(chalk.green(`Done creating "${name}" 🎉`)) + this.log(chalk.green(`Start coding! cd ${targetDirectory}`)) + } + + async catch(error: unknown) { + if (this.spinner?.isSpinning) { + this.spinner.fail() + } + throw error + } +} diff --git a/packages/cli/src/commands/push.ts b/packages/cli/src/commands/push.ts new file mode 100644 index 0000000000..d5d43c287d --- /dev/null +++ b/packages/cli/src/commands/push.ts @@ -0,0 +1,474 @@ +import { Command, flags } from '@oclif/command' +import { DestinationDefinition } from '@segment/actions-core' +import { idToSlug, destinations as actionDestinations } from '@segment/destination-actions' +import chalk from 'chalk' +import { Dictionary, invert, uniq, pick, omit, sortBy } from 'lodash' +import { diffString } from 'json-diff' +import ora from 'ora' +import type { + // @ts-ignore it's ok if CPS isn't available + DestinationMetadata, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataAction, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataActionCreateInput, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataActionFieldCreateInput, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataActionsUpdateInput, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataOptions, + // @ts-ignore it's ok if CPS isn't available + DestinationMetadataUpdateInput, + // @ts-ignore it's ok if CPS isn't available + DestinationSubscriptionPresetFields, + // @ts-ignore it's ok if CPS isn't available + DestinationSubscriptionPresetInput +} from '../lib/control-plane-service' +import { prompt } from '../lib/prompt' +import { OAUTH_OPTIONS, OAUTH_SCHEME, RESERVED_FIELD_NAMES } from '../constants' + +const NOOP_CONTEXT = {} + +type BaseActionInput = Omit + +export default class Push extends Command { + private spinner: ora.Ora = ora() + + static description = `Introspects your integration definition to build and upload your integration to Segment. Requires \`robo stage.ssh\` or \`robo prod.ssh\`.` + + static examples = [`$ segment push`] + + static flags = { + help: flags.help({ char: 'h' }), + force: flags.boolean({ char: 'f' }) + } + + static args = [] + + async run() { + const { flags } = this.parse(Push) + const slugToId = invert(idToSlug) + const availableSlugs = Object.keys(slugToId) + const { chosenSlugs } = await prompt<{ chosenSlugs: string[] }>({ + type: 'multiselect', + name: 'chosenSlugs', + message: 'Integrations:', + choices: availableSlugs.map((s) => ({ + title: s, + value: s + })) + }) + + const destinationIds: string[] = [] + for (const slug of chosenSlugs) { + const id = slugToId[slug] + destinationIds.push(id) + } + + if (!destinationIds.length) { + this.warn(`You must select at least one destination. Exiting...`) + return + } + + this.spinner.start( + `Fetching existing definitions for ${chosenSlugs.map((slug) => chalk.greenBright(slug)).join(', ')}...` + ) + const schemasByDestination = getJsonSchemas(actionDestinations, destinationIds, slugToId) + const [metadatas, actions] = await Promise.all([ + getDestinationMetadatas(destinationIds), + getDestinationMetadataActions(destinationIds) + ]) + + if (metadatas.length !== Object.keys(schemasByDestination).length) { + this.spinner.fail() + throw new Error('Number of metadatas must match number of schemas') + } + + this.spinner.stop() + + for (const metadata of metadatas) { + const schemaForDestination = schemasByDestination[metadata.id] + const slug = schemaForDestination.slug + + this.log('') + this.log(`${chalk.bold.whiteBright(slug)}`) + this.spinner.start(`Generating diff for ${chalk.bold(slug)}...`) + + const actionsToUpdate: DestinationMetadataActionsUpdateInput[] = [] + const actionsToCreate: DestinationMetadataActionCreateInput[] = [] + const existingActions = actions.filter((a) => a.metadataId === metadata.id) + + for (const [actionKey, action] of Object.entries(schemaForDestination.actions)) { + // Note: this implies that changing the slug is a breaking change + const existingAction = existingActions.find((a) => a.slug === actionKey && a.platform === 'cloud') + + const fields: DestinationMetadataActionFieldCreateInput[] = Object.keys(action.fields).map((fieldKey) => { + const field = action.fields[fieldKey] + return { + fieldKey, + type: field.type, + label: field.label, + description: field.description, + defaultValue: field.default, + required: field.required ?? false, + multiple: field.multiple ?? false, + // TODO implement + choices: null, + dynamic: field.dynamic ?? false, + placeholder: field.placeholder ?? '', + allowNull: field.allowNull ?? false + } + }) + + const base: BaseActionInput = { + slug: actionKey, + name: action.title ?? 'Unnamed Action', + description: action.description ?? '', + platform: 'cloud', + hidden: action.hidden ?? false, + defaultTrigger: action.defaultSubscription ?? null, + fields + } + + if (existingAction) { + actionsToUpdate.push({ ...base, actionId: existingAction.id }) + } else { + actionsToCreate.push({ ...base, metadataId: metadata.id }) + } + } + + const options = getOptions(metadata, schemaForDestination) + const basicOptions = getBasicOptions(metadata, options) + const diff = diffString( + asJson({ + basicOptions: filterOAuth(metadata.basicOptions), + options: pick(metadata.options, filterOAuth(Object.keys(options))), + actions: sortBy( + existingActions.map((action) => ({ + ...omit(action, ['id', 'metadataId', 'createdAt', 'updatedAt']), + fields: action.fields?.map((field: object) => + omit(field, ['id', 'metadataActionId', 'sortOrder', 'createdAt', 'updatedAt']) + ) + })), + ['name'] + ) + }), + asJson({ + basicOptions: filterOAuth(basicOptions), + options: pick(options, filterOAuth(Object.keys(options))), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + actions: sortBy( + ([] as Array) + .concat(actionsToUpdate, actionsToCreate) + .map((action) => ({ + ...omit(action, ['id', 'actionId']), + fields: action.fields?.map((field: object) => + omit(field, ['id', 'metadataActionId', 'sortOrder', 'createdAt', 'updatedAt']) + ) + })), + ['name'] + ) + }) + ) + + if (diff) { + this.spinner.warn(`Detected changes for ${chalk.bold(slug)}, please review:`) + this.log(`\n${diff}`) + } else if (flags.force) { + const newDefinition = definitionToJson(schemaForDestination) + this.spinner.warn(`No change detected for ${chalk.bold(slug)}. Using force, please review:`) + this.log(`\n${JSON.stringify(newDefinition, null, 2)}`) + } else { + this.spinner.info(`No change for ${chalk.bold(slug)}. Skipping.`) + continue + } + + const { shouldContinue } = await prompt({ + type: 'confirm', + name: 'shouldContinue', + message: `Publish change for ${slug}?`, + initial: false + }) + + if (!shouldContinue) { + continue + } + + await Promise.all([ + updateDestinationMetadata(metadata.id, { + basicOptions, + options + }), + updateDestinationMetadataActions(actionsToUpdate), + createDestinationMetadataActions(actionsToCreate) + ]) + + const allActions = await getDestinationMetadataActions([metadata.id]) + const presets: DestinationSubscriptionPresetInput[] = [] + + for (const preset of schemaForDestination.presets ?? []) { + const associatedAction = allActions.find((a) => a.slug === preset.partnerAction) + if (!associatedAction) continue + + presets.push({ + actionId: associatedAction.id, + name: preset.name ?? associatedAction.name, + trigger: preset.subscribe, + fields: (preset.mapping as DestinationSubscriptionPresetFields) ?? {} + }) + } + + // We have to wait to do this until after the associated actions are created (otherwise it may fail) + await setSubscriptionPresets(metadata.id, presets) + } + } +} + +function filterOAuth(optionList: string[]) { + return optionList.filter((item) => item !== 'oauth') +} + +function asJson(obj: unknown) { + return JSON.parse(JSON.stringify(obj)) +} + +function definitionToJson(definition: DestinationDefinition) { + // Create a copy that only includes serializable properties + const copy = JSON.parse(JSON.stringify(definition)) + + for (const action of Object.keys(copy.actions)) { + delete copy.actions[action].dynamicFields + delete copy.actions[action].cachedFields + copy.actions[action].hidden = copy.actions[action].hidden ?? false + } + + return copy +} + +function getBasicOptions(metadata: DestinationMetadata, options: DestinationMetadataOptions): string[] { + return uniq([...metadata.basicOptions, ...Object.keys(options)]) +} + +// Note: exporting for testing purposes only +export function getOptions( + metadata: DestinationMetadata, + destinationSchema: DestinationSchema +): DestinationMetadataOptions { + const options: DestinationMetadataOptions = { ...metadata.options } + + // We store all the subscriptions in this legacy field (this will go away when we switch reads to the new tables) + if (!options.subscriptions) { + options.subscriptions = { + default: '', + description: '[{"subscribe":{"type":"track"},"partnerAction":"postToChannel","mapping":{...}}]', + label: 'Subscriptions', + encrypt: false, + hidden: false, + private: false, + scope: 'event_destination', + type: 'string', + validators: [['required', 'The subscriptions setting is required.']] + } + } + + for (const [fieldKey, schema] of Object.entries(destinationSchema.authentication?.fields ?? {})) { + const validators: string[][] = [] + + if (RESERVED_FIELD_NAMES.includes(fieldKey.toLowerCase())) { + throw new Error(`Schema contains a field definition that uses a reserved name: ${fieldKey}`) + } + + if (schema.required) { + validators.push(['required', `The ${fieldKey} property is required.`]) + } + + options[fieldKey] = { + // Authentication-related fields don't support defaults yet + default: '', + description: schema.description, + encrypt: false, + hidden: false, + label: schema.label, + private: true, + scope: 'event_destination', + type: 'string', + validators + } + + // Add oauth settings + if (destinationSchema.authentication?.scheme === OAUTH_SCHEME) { + options['oauth'] = OAUTH_OPTIONS + } + } + + return options +} + +async function getDestinationMetadatas(destinationIds: string[]): Promise { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.getAllDestinationMetadatas(NOOP_CONTEXT, { + byIds: destinationIds + }) + + if (error) { + throw error + } + + if (!data) { + throw new Error('Could not load metadatas') + } + + return data.metadatas +} + +async function getDestinationMetadataActions(destinationIds: string[]): Promise { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.getDestinationMetadataActions(NOOP_CONTEXT, { + metadataIds: destinationIds + }) + + if (error) { + throw error + } + + if (!data) { + throw new Error('Could not load actions') + } + + return data.actions +} + +async function updateDestinationMetadata( + destinationId: string, + input: DestinationMetadataUpdateInput +): Promise { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.updateDestinationMetadata(NOOP_CONTEXT, { + destinationId, + input + }) + + if (error) { + console.log(error) + throw error + } + + if (!data) { + throw new Error('Could not update metadata') + } + + return data.metadata +} + +async function setSubscriptionPresets(metadataId: string, presets: DestinationSubscriptionPresetInput[]) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.setDestinationSubscriptionPresets(NOOP_CONTEXT, { + metadataId, + presets + }) + + if (error) { + console.log(error) + throw error + } + + if (!data) { + throw new Error('Could not set subscription presets') + } + + return data.presets +} + +async function createDestinationMetadataActions( + input: DestinationMetadataActionCreateInput[] +): Promise { + if (!input.length) return [] + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.createDestinationMetadataActions(NOOP_CONTEXT, { + input + }) + + if (error) { + console.log(error) + throw error + } + + if (!data) { + throw new Error('Could not create metadata actions') + } + + return data.actions +} + +async function updateDestinationMetadataActions( + input: DestinationMetadataActionsUpdateInput[] +): Promise { + if (!input.length) return [] + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.updateDestinationMetadataActions(NOOP_CONTEXT, { + input + }) + + if (error) { + throw error + } + + if (!data) { + throw new Error('Could not update metadata actions') + } + + return data.actions +} + +interface SchemasByDestination { + [destinationId: string]: DestinationSchema +} + +export interface DestinationSchema extends DestinationDefinition { + slug: string +} + +function getJsonSchemas( + destinations: Record>, + destinationIds: string[], + slugToId: Dictionary +): SchemasByDestination { + const schemasByDestination: SchemasByDestination = {} + + for (const destinationSlug in destinations) { + const destinationId = slugToId[destinationSlug] + if (!destinationIds.includes(destinationId)) { + continue + } + + const definition = destinations[destinationSlug] + + schemasByDestination[destinationId] = { + ...definition, + slug: destinationSlug + } + } + + return schemasByDestination +} diff --git a/packages/cli/src/commands/register.ts b/packages/cli/src/commands/register.ts new file mode 100644 index 0000000000..cfd91b4e6b --- /dev/null +++ b/packages/cli/src/commands/register.ts @@ -0,0 +1,152 @@ +import { Command, flags } from '@oclif/command' +import globby from 'globby' +import ora from 'ora' +import path from 'path' +import slugify from 'slugify' +import { loadDestination } from '../lib/destinations' +// @ts-ignore it's ok shhh +import type { CreateDestinationMetadataInput, DestinationMetadataOptions } from '../lib/control-plane-service' +import { autoPrompt } from '../lib/prompt' + +const NOOP_CONTEXT = {} + +export default class Register extends Command { + private spinner: ora.Ora = ora() + + static description = `Creates a new integration on Segment.` + + static examples = [`$ segment register`] + + static flags = { + help: flags.help({ char: 'h' }) + } + + async run() { + const { flags } = this.parse(Register) + + // TODO support a command flag for this + const integrationsGlob = './packages/destination-actions/src/destinations/*' + const integrationDirs = await globby(integrationsGlob, { + expandDirectories: false, + onlyDirectories: true, + gitignore: true, + ignore: ['node_modules'] + }) + + const { selectedDestination } = await autoPrompt<{ selectedDestination: { path: string; name: string } }>(flags, { + type: 'select', + name: 'selectedDestination', + message: 'Which integration?', + choices: integrationDirs.map((integrationPath) => { + const [name] = integrationPath.split(path.sep).reverse() + return { + title: name, + value: { path: integrationPath, name } + } + }) + }) + + if (!selectedDestination) { + this.warn('You must choose a destination. Exiting.') + return + } + + this.spinner.start(`Introspecting definition`) + const destination = await loadDestination(selectedDestination.path) + + if (!destination) { + this.spinner.fail() + this.warn('No destination definition found. Exiting.') + return + } else { + this.spinner.succeed() + } + + const name = `Actions ${destination.name}` + const slug = slugify(destination.slug ?? name).toLowerCase() + + if (destination.slug && destination.slug !== slug) { + this.warn(`Your destination slug does not meet the requirements. Try \`${slug}\` instead`) + return + } + + // Ensure we don't already have a destination with this slug... + this.spinner.start(`Checking availability for ${slug}`) + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { controlPlaneService } = require('../lib/control-plane-service') + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const { data, error } = await controlPlaneService.getDestinationMetadataBySlug(NOOP_CONTEXT, { slug }) + if (error?.statusCode === 404) { + this.spinner.succeed() + } else if (error) { + this.spinner.fail() + this.error(`Error checking availablity for ${slug}: ${error.message}`) + } else if (data?.metadata) { + this.spinner.fail() + this.error( + `There is already a destination registered for ${slug} named "${data.metadata.name}" that was created ${data.metadata.createdAt}.` + ) + } + + this.spinner.start(`Preparing destination definition`) + + // We store the destination-level JSON Schema in an option with key `metadata` + // Currently this is needed to render the UI views for action destinations + const initialOptions: DestinationMetadataOptions = { + // This setting is required until we switch off the legacy "data model" + subscriptions: { + label: 'subscriptions', + type: 'string', + scope: 'event_destination', + private: false, + encrypt: false, + hidden: false, + validators: [['required', `The subscriptions property is required.`]] + } + } + + const definition: CreateDestinationMetadataInput['input'] = { + name, + slug, + type: 'action_destination', + description: destination.description ?? '', + status: 'PRIVATE_BUILDING', + methods: { + pageview: true, + identify: true, + alias: true, + track: true, + group: true + }, + platforms: { + browser: false, + mobile: false, + server: true + }, + options: initialOptions, + basicOptions: Object.keys(initialOptions) + } + + this.spinner.succeed() + this.log(`Here is the JSON you need to use to create your destination in Partner Portal:`) + this.log(JSON.stringify(definition, null, 2)) + + // TODO actually create the destination + // const { data, error } = await controlPlaneService.createDestinationMetadata(NOOP_CONTEXT, { + // input: { + // options: metadata.options, + // } + // }) + + return + } + + async catch(error: unknown) { + if (this.spinner?.isSpinning) { + this.spinner.fail() + } + throw error + } +} diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts new file mode 100644 index 0000000000..10ad3e9321 --- /dev/null +++ b/packages/cli/src/constants.ts @@ -0,0 +1,83 @@ +// @ts-ignore it's ok shhh +import { DestinationMetadataOption } from './lib/control-plane-service' + +export const RESERVED_FIELD_NAMES = [ + 'oauth', + 'oauth2', + 'accesstoken', + 'access-token', + 'acccess_token', + 'refresh-token', + 'refresh_token', + 'token_type' +] + +export const OAUTH_SCHEME = 'oauth2' + +export const OAUTH_OPTIONS: DestinationMetadataOption = { + default: {}, + description: 'Authorizes Segment to OAuth to the Destination API', + encrypt: false, + hidden: true, + label: 'OAuth', + private: true, + scope: 'event_destination', + type: 'oauth', + fields: [ + { + 'access-token': { + description: 'The (legacy) access token provided by Destination API after the OAuth handshake.', + type: 'string' + }, + access_token: { + description: 'The access token provided by Destination API after the OAuth handshake.', + type: 'string' + }, + appId: { + description: 'The App ID, retrieved via Destination API post-auth.', + type: 'string' + }, + appName: { + description: + 'The authorized user App, as represented in the integration UI, retrieved via Destination API on settings view load and cached in settings.', + type: 'string' + }, + createdAt: { + description: 'Date of OAuth connection.', + type: 'string' + }, + createdBy: { + description: 'Email address of segment user who connected OAuth.', + type: 'string' + }, + displayName: { + description: + 'The authorized user, as represented in the integration UI, retrieved via Destination API on settings view load and cached in settings.', + type: 'string' + }, + refresh_token: { + description: 'The refresh token provided by Destination API after the OAuth handshake.', + type: 'string' + }, + token_type: { + description: '', + type: 'string' + } + } + ] +} + +export const OAUTH_GENERATE_TYPES = { + accessToken: { + label: 'Access Token', + type: 'string', + required: true, + description: 'Token issued by the partner API after verifying the identity of the user account' + }, + refreshToken: { + label: 'Refresh Token', + type: 'string', + description: + 'Token provided by the partner API that can be used to request a fresh access token from the authorization server' + } +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 0000000000..088abcf778 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1 @@ +export { run } from '@oclif/command' diff --git a/packages/cli/src/lib/codemods.ts b/packages/cli/src/lib/codemods.ts new file mode 100644 index 0000000000..f0880cc606 --- /dev/null +++ b/packages/cli/src/lib/codemods.ts @@ -0,0 +1,67 @@ +import jscodeshift from 'jscodeshift' +import addImports from 'jscodeshift-add-imports' +import prettier from 'prettier' + +const j = jscodeshift.withParser('ts') +const { statement } = j.template + +const prettierOptions = prettier.resolveConfig.sync(process.cwd()) + +export function format(code: string): string { + return prettier.format(code, { parser: 'typescript', ...prettierOptions }) +} + +/** + * Modifies a property of the default export with a new key + */ +export function addKeyToDefaultExport(code: string, property: string, variableName: string) { + const root = j(code) + + const newProperty = j.property.from({ + kind: 'init', + key: j.identifier(variableName), + value: j.identifier(variableName), + shorthand: true + }) + + const defaultExport = root.find(j.ExportDefaultDeclaration) + if (!defaultExport.length) { + throw new Error('Could not parse default export.') + } + + let objectToModify = defaultExport.get().node.declaration + if (objectToModify.type === 'Identifier') { + // We need to find the original variable to modify + const exportedVar = root.find(j.VariableDeclaration, { + declarations: [{ id: { type: 'Identifier', name: objectToModify.name } }] + }) + if (!exportedVar.length) { + throw new Error('Unable to find exported variable to modify.') + } + + objectToModify = exportedVar.get().node.declarations[0].init + } + + if (objectToModify.type !== 'ObjectExpression') { + throw new Error(`Invalid default export type: "${objectToModify.type}"`) + } + + // Make sure the object doesn't already have th property + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any + const existingProperty = objectToModify.properties.find((props: any) => props.key.name === property) + if (existingProperty) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any + if (!existingProperty.value.properties.find((props: any) => props.key.name === variableName)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + existingProperty.value.properties.push(newProperty) + } + } else { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + objectToModify.properties.push(j.property('init', j.identifier(property), j.objectExpression([newProperty]))) + } + + const importStatement = `import ${variableName} from './${variableName}'` + addImports(root, [statement([importStatement])]) + + return format(root.toSource()) +} diff --git a/packages/cli/src/lib/control-plane-service.ts b/packages/cli/src/lib/control-plane-service.ts new file mode 100644 index 0000000000..648827b42f --- /dev/null +++ b/packages/cli/src/lib/control-plane-service.ts @@ -0,0 +1,16 @@ +// @ts-ignore it's ok if CPS isn't available +import ControlPlaneService from '@segment/control-plane-service-client' +// @ts-ignore it's ok if CPS isn't available +export * from '@segment/control-plane-service-client' + +export const controlPlaneService = new ControlPlaneService({ + name: 'control-plane-service', + url: 'http://control-plane-service.segment.local', + userAgent: 'Segment (actions cli)', + timeout: 10000, + headers: { + // All calls from this script are system-to-system and shouldn't require authz checks + // TODO remove this once we support auth tokens + 'skip-authz': '1' + } +}) diff --git a/packages/cli/src/lib/destinations.ts b/packages/cli/src/lib/destinations.ts new file mode 100644 index 0000000000..41e359a775 --- /dev/null +++ b/packages/cli/src/lib/destinations.ts @@ -0,0 +1,24 @@ +import { DestinationDefinition } from '@segment/actions-core' +import path from 'path' +import { clearRequireCache } from './require-cache' + +/** Attempts to load a destination definition from a given file path */ +export async function loadDestination(filePath: string): Promise { + const importPath = path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath) + + // Clear Node's require cache to pick up any changes + clearRequireCache() + + // Import the file, assert that it's a destination definition entrypoint + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(importPath) + // look for `default` or `destination` export + const destination = module.destination || module.default + + // Loose validation on a destination definition + if (!destination?.name || typeof destination?.actions !== 'object') { + return null + } + + return destination as DestinationDefinition +} diff --git a/packages/cli/src/lib/prompt.ts b/packages/cli/src/lib/prompt.ts new file mode 100644 index 0000000000..701daab9dd --- /dev/null +++ b/packages/cli/src/lib/prompt.ts @@ -0,0 +1,44 @@ +import { flags } from '@oclif/command' +import prompts from 'prompts' + +const onCancel = () => { + process.exit(0) +} + +// TODO fix these types +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function prompt( + questions: prompts.PromptObject | Array, + options: prompts.Options = {} +): Promise { + return prompts(questions, { onCancel, ...options }) as Promise +} + +/** + * Given a set of parsed flags and questions, only prompt for answers that are missing + */ +export async function autoPrompt( + flags: Partial & { [key: string]: unknown }, + questions: prompts.PromptObject | Array +) { + if (!Array.isArray(questions)) { + questions = [questions] + } + + for (const question of questions) { + const name = question.name as string + if (typeof flags[name] !== 'undefined') { + // Set the prompt not to appear + question.type = null + question.initial = flags[name] + } + } + + const answers = await prompt(questions) + + return { + ...flags, + // TODO fix the types to be accurate + ...(answers as F) + } +} diff --git a/packages/cli/src/lib/require-cache.ts b/packages/cli/src/lib/require-cache.ts new file mode 100644 index 0000000000..51049a928c --- /dev/null +++ b/packages/cli/src/lib/require-cache.ts @@ -0,0 +1,5 @@ +export function clearRequireCache() { + Object.keys(require.cache).forEach((key) => { + delete require.cache[key] + }) +} diff --git a/packages/cli/src/lib/templates.ts b/packages/cli/src/lib/templates.ts new file mode 100644 index 0000000000..2e7e034075 --- /dev/null +++ b/packages/cli/src/lib/templates.ts @@ -0,0 +1,31 @@ +import fs from 'fs-extra' +import Mustache from 'mustache' +import path from 'path' + +export function renderTemplate(template: string, data?: unknown) { + return Mustache.render(template, data) +} + +export function renderTemplates(templatePath: string, targetDir: string, data: unknown = {}, force?: boolean) { + if (fs.existsSync(targetDir)) { + if (!force && fs.readdirSync(targetDir).length > 0) { + throw new Error(`There's already content in ${targetDir}. Exiting.`) + } + } else { + fs.mkdirSync(targetDir) + } + + let files: string[] + if (fs.statSync(templatePath).isFile()) { + files = [templatePath] + } else { + files = fs.readdirSync(templatePath) + } + + for (const file of files) { + const template = fs.readFileSync(path.join(templatePath, file), 'utf8') + const contents = renderTemplate(template, data) + const writePath = path.join(targetDir, file) + fs.writeFileSync(writePath, contents, 'utf8') + } +} diff --git a/packages/cli/templates/actions/empty-action/index.ts b/packages/cli/templates/actions/empty-action/index.ts new file mode 100644 index 0000000000..1a969eee65 --- /dev/null +++ b/packages/cli/templates/actions/empty-action/index.ts @@ -0,0 +1,15 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: '{{name}}', + description: '{{description}}', + defaultSubscription: 'type = "track"', + fields: {}, + perform: (_request, _data) => { + // Make your partner api request here! + } +} + +export default action diff --git a/packages/cli/templates/actions/empty-browser-action/index.ts b/packages/cli/templates/actions/empty-browser-action/index.ts new file mode 100644 index 0000000000..5e39e77247 --- /dev/null +++ b/packages/cli/templates/actions/empty-browser-action/index.ts @@ -0,0 +1,15 @@ +import type { BrowserActionDefinition } from '../../../lib/browser-destinations' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +// Change from unknown to the partner SDK types +const action: BrowserActionDefinition = { + title: '{{name}}', + description: '{{description}}', + fields: {}, + perform: (_client) => { + // Invoke Partner SDK here + } +} + +export default action diff --git a/packages/cli/templates/destinations/basic-auth/index.ts b/packages/cli/templates/destinations/basic-auth/index.ts new file mode 100644 index 0000000000..39f046c35c --- /dev/null +++ b/packages/cli/templates/destinations/basic-auth/index.ts @@ -0,0 +1,35 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: '{{name}}', + authentication: { + scheme: 'basic', + fields: { + username: { + label: 'Username', + description: 'Your {{name}} username', + type: 'string', + required: true + }, + password: { + label: 'password', + description: 'Your {{name}} password.', + type: 'string', + required: true + } + }, + testAuthentication: (_request) => { + // Return a request that tests/validates the user's credentials here + } + }, + extendRequest({ settings }) { + return { + username: settings.username, + password: settings.password + } + }, + actions: {} +} + +export default destination diff --git a/packages/cli/templates/destinations/browser/index.ts b/packages/cli/templates/destinations/browser/index.ts new file mode 100644 index 0000000000..87431ff1b1 --- /dev/null +++ b/packages/cli/templates/destinations/browser/index.ts @@ -0,0 +1,22 @@ +import type { Settings } from './generated-types' +import type { BrowserDestinationDefinition } from '../../lib/browser-destinations' +import { browserDestination } from '../../runtime/shim' +import { loadScript } from '../../runtime/load-script' + +// Switch from unknown to the partner SDK client types +export const destination: BrowserDestinationDefinition = { + name: '{{name}}', + + authentication: { + fields: { + // Add any Segment destination settings required here + } + }, + actions: {}, + initialize: async ({ settings, analytics }) => { + await loadScript('') + // initialize client code here + } +} + +export default browserDestination(destination) diff --git a/packages/cli/templates/destinations/custom-auth/index.ts b/packages/cli/templates/destinations/custom-auth/index.ts new file mode 100644 index 0000000000..346bdcc352 --- /dev/null +++ b/packages/cli/templates/destinations/custom-auth/index.ts @@ -0,0 +1,16 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: '{{name}}', + authentication: { + scheme: 'custom', + fields: {}, + testAuthentication: (_request) => { + // Return a request that tests/validates the user's authentication fields here + } + }, + actions: {} +} + +export default destination diff --git a/packages/cli/templates/destinations/minimal/index.ts b/packages/cli/templates/destinations/minimal/index.ts new file mode 100644 index 0000000000..659dcc97e0 --- /dev/null +++ b/packages/cli/templates/destinations/minimal/index.ts @@ -0,0 +1,9 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: '{{name}}', + actions: {} +} + +export default destination diff --git a/packages/cli/templates/destinations/oauth2-auth/index.ts b/packages/cli/templates/destinations/oauth2-auth/index.ts new file mode 100644 index 0000000000..87836e20a2 --- /dev/null +++ b/packages/cli/templates/destinations/oauth2-auth/index.ts @@ -0,0 +1,37 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: '{{name}}', + authentication: { + scheme: 'oauth2', + fields: {}, + testAuthentication: (_request) => { + // Return a request that tests/validates the user's credentials here + }, + refreshAccessToken: async (request, { settings }, oauthConfig) => { + // Return a request that refreshes the access_token if the API supports it + const res = await request('https://www.example.com/oauth/refresh', { + method: 'POST', + body: new URLSearchParams({ + refresh_token: settings.refreshToken, + client_id: oauthConfig.clientId, + client_secret: oauthConfig.clientSecret, + grant_type: 'refresh_token' + }) + }) + + return { accessToken: res.body.access_token } + } + }, + extendRequest({ settings }) { + return { + headers: { + authorization: `Bearer ${settings.accessToken}` + } + } + }, + actions: {} +} + +export default destination diff --git a/packages/cli/tsconfig.build.json b/packages/cli/tsconfig.build.json new file mode 100644 index 0000000000..590b2f90f6 --- /dev/null +++ b/packages/cli/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "lib": ["es2018"], + "declaration": true, + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "sourceMap": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true, + "skipLibCheck": true, + "importHelpers": true, + "outDir": "dist" + }, + "exclude": ["**/__tests__/**/*.ts"], + "include": ["src"] +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000000..93ca5ca821 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "lib": ["es2018"], + "declaration": true, + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "sourceMap": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDirs": ["src"], + "importHelpers": true, + "baseUrl": "./", + "paths": { + "@segment/actions-core": ["../core/src"], + "@segment/actions-core/*": ["../core/src/*"], + "@segment/destination-actions": ["../destination-actions/src"], + "@segment/destination-subscriptions": ["../destination-subscriptions/src"] + } + }, + "include": ["src"] +} diff --git a/packages/core/benchmarks/fql.js b/packages/core/benchmarks/fql.js new file mode 100644 index 0000000000..ae6390776c --- /dev/null +++ b/packages/core/benchmarks/fql.js @@ -0,0 +1,35 @@ +const { add, complete, cycle, suite } = require('benny') +const { parseFql, validate } = require('@segment/destination-subscriptions') + +const event = { + type: 'track', + event: 'Sweater On', + context: { + library: { + name: 'analytics.js', + version: '2.11.1' + } + }, + properties: { + neighborhood: 'Latrobe', + noun: 'neighbor', + sweaterColor: 'red' + } +} + +const addFqlCase = (fql) => add(fql, () => { + validate(parseFql(fql), event) +}) + +module.exports = suite( + 'FQL Parsing', + + addFqlCase('type = "track"'), + addFqlCase('type = "nope"'), + addFqlCase('contains(properties.neighborhood, "Lat")'), + addFqlCase('match(properties.noun, "*bor")'), + addFqlCase('properties.sweaterColor = "red"'), + + cycle(), + complete() +) diff --git a/packages/core/benchmarks/mapping-kit.js b/packages/core/benchmarks/mapping-kit.js new file mode 100644 index 0000000000..81f5153d22 --- /dev/null +++ b/packages/core/benchmarks/mapping-kit.js @@ -0,0 +1,47 @@ +const { add, complete, cycle, suite } = require('benny') +const { transform } = require('@segment/actions-core') + +const event = { + type: 'track', + event: 'Sweater On', + context: { + library: { + name: 'analytics.js', + version: '2.11.1' + } + }, + properties: { + neighborhood: 'Latrobe', + noun: 'neighbor', + sweaterColor: 'red' + } +} + +module.exports = suite( + 'Mapping Kit Transforms', + + add('@path directive', () => { + transform({ '@path': '$.properties.neighborhood' }, event) + }), + + add('@template directive', () => { + transform({ '@template': '{{properties.neighborhood}}' }, event) + }), + + add('multiple directives', () => { + transform({ + 'foo': 'bar', + 'baz': { '@path': '$.properties.biz' }, + 'qux': { + '@if': { + 'exists': { '@path': '$.properties.noun' }, + 'then': 'yep', + 'else': 'nope' + } + } + }, event) + }), + + cycle(), + complete() +) diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000000..a01edaf4ba --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,72 @@ +{ + "name": "@segment/actions-core", + "description": "Core runtime for Destinations Actions.", + "version": "2.2.1", + "repository": { + "type": "git", + "url": "https://github.com/segmentio/action-destinations", + "directory": "packages/core" + }, + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "exports": { + ".": { + "require": "./dist/cjs/index.js", + "default": "./dist/esm/index.js" + }, + "./": { + "require": "./dist/cjs/", + "default": "./dist/esm/" + } + }, + "types": "dist/esm/index.d.ts", + "files": [ + "dist", + "package.json" + ], + "engineStrict": true, + "license": "UNLICENSED", + "publishConfig": { + "access": "restricted", + "registry": "https://registry.npmjs.org" + }, + "scripts": { + "build": "yarn clean && yarn build:cjs && yarn build:esm", + "build:cjs": "yarn tsc -p tsconfig.build.json -m commonjs --outDir dist/cjs", + "build:esm": "yarn tsc -p tsconfig.build.json -m es2015 --outDir dist/esm", + "clean": "tsc -b tsconfig.build.json --clean", + "postclean": "rm -rf dist", + "prepublishOnly": "yarn build", + "test": "jest", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/btoa-lite": "^1.0.0", + "@types/express": "^4.17.11", + "@types/jest": "^26.0.23", + "@types/json-schema": "^7.0.7", + "benny": "^3.6.15", + "create-test-server": "^3.0.1", + "jest": "^26.6.3" + }, + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "@segment/ajv-human-errors": "^1.1.0", + "@segment/destination-subscriptions": "^3.0.0", + "abort-controller": "^3.0.0", + "aggregate-error": "^3.1.0", + "ajv": "^6.12.4", + "btoa-lite": "^1.0.0", + "cross-fetch": "^3.1.4", + "dayjs": "^1.10.4", + "node-cache": "^5.1.2", + "ts-custom-error": "^3.2.0" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "modulePathIgnorePatterns": [ + "/dist/" + ] + } +} diff --git a/packages/core/src/__tests__/create-request-client.test.ts b/packages/core/src/__tests__/create-request-client.test.ts new file mode 100644 index 0000000000..cca050800d --- /dev/null +++ b/packages/core/src/__tests__/create-request-client.test.ts @@ -0,0 +1,114 @@ +import btoa from 'btoa-lite' +import createTestServer from 'create-test-server' +import createRequestClient from '../create-request-client' + +describe('createRequestClient', () => { + it('should create a request client instance that has Segment defaults', async () => { + const log: Record = {} + + const request = createRequestClient({ + afterResponse: [ + (request, options, response) => { + log.request = request + log.options = options + log.response = response + } + ] + }) + + const server = await createTestServer() + server.get('/', (_request, response) => { + response.write('Hello world!') + response.end() + }) + + await request(server.url) + expect(log.request.headers.get('user-agent')).toBe('Segment') + expect(log.options.timeout).toBe(10000) + await server.close() + }) + + it('should merge custom options when creating the request client instance', async () => { + const log: Record = {} + + const request = createRequestClient({ + headers: { Authorization: `Bearer supersekret` }, + afterResponse: [ + (request, options, response) => { + log.request = request + log.options = options + log.response = response + } + ] + }) + + const server = await createTestServer() + server.get('/', (_request, response) => { + response.json({ greeting: 'Yo' }) + }) + + const response = await request(server.url, { headers: { 'user-agent': 'foo' } }) + expect(await response.json()).toMatchObject({ greeting: 'Yo' }) + expect(response.url).toBe(`${server.url}/`) + expect(log.request).toBeDefined() + expect(log.request.url).toBe(`${server.url}/`) + expect(log.request.headers.get('user-agent')).toBe('foo') + expect(log.request.headers.get('authorization')).toBe('Bearer supersekret') + await server.close() + }) + + it('should automatically base64 encode username:password', async () => { + const log: Record = {} + + const request = createRequestClient({ + afterResponse: [ + (request, options, response) => { + log.request = request + log.options = options + log.response = response + } + ] + }) + + const server = await createTestServer() + server.get('/', (_request, response) => { + response.json({ greeting: 'Yo' }) + }) + + await request(server.url, { username: 'foo', password: 'bar' }) + expect(log.request.headers.get('authorization')).toBe(`Basic ${btoa('foo:bar')}`) + await server.close() + }) + + it('`response.data` should contain the json parsed body when content-type is application/json', async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.json({ hello: 'world' }) + }) + + const request = createRequestClient() + + await expect(request(server.url, { method: 'post', json: { foo: true } })).resolves.toMatchObject({ + data: expect.objectContaining({ hello: 'world' }) + }) + await server.close() + }) + + it('`response.data` should be null if parsing fails when content-type is application/json', async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.set('Content-Type', 'application/json') + // lies! + response.write('') + response.end() + }) + + const request = createRequestClient() + + await expect(request(server.url, { method: 'post', json: { foo: true } })).resolves.toMatchObject({ + data: undefined, + content: '' + }) + await server.close() + }) +}) diff --git a/packages/core/src/__tests__/destination-kit.test.ts b/packages/core/src/__tests__/destination-kit.test.ts new file mode 100644 index 0000000000..9c1a31b274 --- /dev/null +++ b/packages/core/src/__tests__/destination-kit.test.ts @@ -0,0 +1,153 @@ +import { Destination, DestinationDefinition } from '../destination-kit' +import { JSONObject } from '../json-object' +import { SegmentEvent } from '../segment-event' + +const destinationCustomAuth: DestinationDefinition = { + name: 'Google Analytics 4', + authentication: { + scheme: 'custom', + fields: { + apiSecret: { + label: 'API secret', + description: 'Api key', + type: 'string', + required: true + } + }, + testAuthentication: (_request) => { + return true + } + }, + actions: { + customEvent: { + title: 'Send a Custom Event', + description: 'Send events to a custom event in API', + defaultSubscription: 'type = "track"', + fields: {}, + perform: (_request) => { + return 'this is a test' + } + } + } +} + +const destinationOAuth2: DestinationDefinition = { + name: 'Google Analytics 4', + authentication: { + scheme: 'oauth2', + fields: { + apiSecret: { + label: 'API secret', + description: 'Api key', + type: 'string', + required: true + } + }, + testAuthentication: (_request) => { + return true + }, + refreshAccessToken: (_request) => { + return new Promise((resolve, _reject) => { + setTimeout(() => { + resolve({ accessToken: 'fresh-token' }) + }, 3) + }) + } + }, + actions: { + customEvent: { + title: 'Send a Custom Event', + description: 'Send events to a custom event in API', + defaultSubscription: 'type = "track"', + fields: {}, + perform: (_request) => { + return 'this is a test' + } + } + } +} + +describe('destination kit', () => { + describe('event validations', () => { + test('should return `invalid subscription` when sending an empty subscribe', async () => { + const destinationTest = new Destination(destinationCustomAuth) + const testEvent: SegmentEvent = { type: 'track' } + const testSettings = { subscription: { subscribe: '', partnerAction: 'customEvent' } } + const res = await destinationTest.onEvent(testEvent, testSettings) + expect(res).toEqual([{ output: 'invalid subscription' }]) + }) + + test('should return invalid subscription with details when sending an invalid subscribe', async () => { + const destinationTest = new Destination(destinationCustomAuth) + const testEvent: SegmentEvent = { type: 'track' } + const testSettings = { subscription: { subscribe: 'typo', partnerAction: 'customEvent' } } + const res = await destinationTest.onEvent(testEvent, testSettings) + expect(res).toEqual([{ output: "invalid subscription : Cannot read property 'type' of undefined" }]) + }) + + test('should return `not subscribed` when providing an empty event', async () => { + const destinationTest = new Destination(destinationCustomAuth) + const testSettings = { subscription: { subscribe: 'type = "track"', partnerAction: 'customEvent' } } + // @ts-ignore needed for replicating empty event at runtime + const res = await destinationTest.onEvent({}, testSettings) + expect(res).toEqual([{ output: 'not subscribed' }]) + }) + + test('should succeed if provided with a valid event & settings', async () => { + const destinationTest = new Destination(destinationCustomAuth) + const testEvent: SegmentEvent = { + properties: { field_one: 'test input' }, + userId: '3456fff', + type: 'track' + } + const testSettings = { + apiSecret: 'test_key', + subscription: { + subscribe: 'type = "track"', + partnerAction: 'customEvent', + mapping: { + clientId: '23455343467', + name: 'fancy_event', + parameters: { field_one: 'rogue one' } + } + } + } + + const res = await destinationTest.onEvent(testEvent, testSettings) + expect(res).toEqual([ + { output: 'MapInput completed', error: null }, + { output: 'Validate completed', error: null }, + { output: 'this is a test', error: null } + ]) + }) + }) + + describe('refresh token', () => { + test('should throw a `NotImplemented` error', async () => { + const destinationTest = new Destination(destinationCustomAuth) + const testSettings = { subscription: { subscribe: '', partnerAction: 'customEvent' } } + try { + await destinationTest.refreshAccessToken(testSettings, { + clientId: 'test-clientid', + clientSecret: 'test-clientsecret' + }) + fail('test should have thrown a NotImplemented error') + } catch (e) { + expect(e.status).toEqual(501) + expect(e.message).toEqual('refreshAccessToken is only valid with oauth2 authentication scheme') + expect(e.code).toEqual('NotImplemented') + } + }) + + test('should throw a `NotImplemented` error', async () => { + const destinationTest = new Destination(destinationOAuth2) + const testSettings = { subscription: { subscribe: 'type = "track"', partnerAction: 'customEvent' } } + const res = await destinationTest.refreshAccessToken(testSettings, { + clientId: 'test-clientid', + clientSecret: 'test-clientsecret' + }) + + expect(res).toEqual({ accessToken: 'fresh-token' }) + }) + }) +}) diff --git a/packages/core/src/__tests__/get.test.ts b/packages/core/src/__tests__/get.test.ts new file mode 100644 index 0000000000..faa436ba08 --- /dev/null +++ b/packages/core/src/__tests__/get.test.ts @@ -0,0 +1,43 @@ +import { get } from '../get' + +const obj = { + a: { + b: { + c: 42, + d: true, + e: 'hello', + f: [{ g: 'yay' }, { g: 'nay' }] + }, + h: null + }, + u: undefined +} + +const fixtures: Record = { + a: obj.a, + 'a.b': obj.a.b, + 'a.b.c': obj.a.b.c, + 'a.b.d': obj.a.b.d, + 'a.b.e': obj.a.b.e, + 'a.b.f[0]': obj.a.b.f[0], + 'a.b.f[0].g': obj.a.b.f[0].g, + 'a.h': obj.a.h, + 'a.b.x': undefined, + u: undefined +} + +describe('get', () => { + for (const path of Object.keys(fixtures)) { + test(`"${path}"`, () => { + expect(get(obj, path)).toEqual(fixtures[path]) + }) + + test(`"${path}" with default`, () => { + if (fixtures[path] === undefined) { + expect(get(obj, path, 'default')).toEqual('default') + } else { + expect(get(obj, path, 'default')).not.toEqual('default') + } + }) + } +}) diff --git a/packages/core/src/__tests__/request-client.test.ts b/packages/core/src/__tests__/request-client.test.ts new file mode 100644 index 0000000000..0b1f962c69 --- /dev/null +++ b/packages/core/src/__tests__/request-client.test.ts @@ -0,0 +1,453 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import AbortController from 'abort-controller' +import createTestServer from 'create-test-server' +import { URLSearchParams } from 'url' +import createInstance from '../request-client' +import { Response } from '../fetch' + +describe('createInstance', () => { + it('should create a new request client instance', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url)).ok).toBe(true) + await server.close() + }) + + it('should merge options with instance defaults', async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.end() + }) + + let finalOptions: any + const request = createInstance({ + method: 'get', + afterResponse: [ + (_request, options) => { + finalOptions = options + } + ] + }) + + expect((await request(server.url, { method: 'post' })).ok).toBe(true) + expect(finalOptions.method).toBe('POST') + await server.close() + }) + + it('should dedupe headers when merging options', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let finalOptions: any + const request = createInstance({ + method: 'get', + headers: { + 'content-type': 'application/json' + }, + afterResponse: [ + (_request, options) => { + finalOptions = options + } + ] + }) + + expect((await request(server.url, { headers: { 'Content-Type': 'text/plain' } })).ok).toBe(true) + expect(finalOptions.headers.get('content-type')).toBe('text/plain') + await server.close() + }) +}) + +describe('request.extend()', () => { + it('should allow extending an existing instance with new defaults', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let opts: any + + const request = createInstance({ + timeout: 5000 + }).extend({ + timeout: 8000, + afterResponse: [ + (_request, options) => { + opts = options + } + ] + }) + + expect((await request(server.url)).ok).toBe(true) + expect(opts.timeout).toBe(8000) + await server.close() + }) + + it('should concatenate arrays when merging options', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let calledFirst = false + let calledSecond = false + + const request = createInstance({ + beforeRequest: [ + () => { + calledFirst = true + } + ] + }).extend({ + beforeRequest: [ + () => { + calledSecond = true + } + ] + }) + + expect((await request(server.url)).ok).toBe(true) + expect(calledFirst).toBe(true) + expect(calledSecond).toBe(true) + await server.close() + }) +}) + +describe('request()', () => { + it('should perform GET requests', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url, { method: 'get' })).ok).toBe(true) + await server.close() + }) + + it('should perform POST requests', async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url, { method: 'post' })).ok).toBe(true) + await server.close() + }) + + it('should perform PUT requests', async () => { + const server = await createTestServer() + server.put('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url, { method: 'put' })).ok).toBe(true) + await server.close() + }) + + it('should perform DELETE requests', async () => { + const server = await createTestServer() + server.delete('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url, { method: 'delete' })).ok).toBe(true) + await server.close() + }) + + it('should perform HEAD requests', async () => { + const server = await createTestServer() + server.head('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + expect((await request(server.url, { method: 'head' })).ok).toBe(true) + await server.close() + }) + + it('`method` should automatically get upcased for consistency', async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.end() + }) + + let options: any + const request = createInstance({ + beforeRequest: [ + (requestOptions) => { + options = requestOptions + } + ] + }) + + expect((await request(server.url, { method: 'post' })).ok).toBe(true) + expect(options.method).toBe('POST') + await server.close() + }) + + it('`timeout` should abort requests when a timeout occurs', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + setTimeout(() => { + response.end() + }, 1000) + }) + + let called = false + const request = createInstance({ + afterResponse: [ + () => { + called = true + } + ] + }) + + await expect(request(server.url, { timeout: 500 })).rejects.toThrowError('Request timed out') + expect(called).toBe(false) + await server.close() + }) + + it('`timeout` should throw when a timeout occurs before a response is received', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + setTimeout(() => { + response.end() + }, 1000) + }) + + const request = createInstance() + + await expect(request(server.url, { timeout: 500 })).rejects.toThrowError('Request timed out') + await server.close() + }) + + it('`timeout` should abort the request when the provided `signal` is aborted', async () => { + const controller = new AbortController() + const server = await createTestServer() + server.get('/', (_request, response) => { + setTimeout(() => { + response.end() + }, 10000) + }) + + const request = createInstance() + + // input signal gets aborted before request + setTimeout(() => { + controller.abort() + }, 500) + + await expect(request(server.url, { signal: controller.signal })).rejects.toThrowError('The user aborted a request.') + await server.close() + }) + + it('`timeout` should be cleared if the response succeeds before the timeout', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + const request = createInstance() + + await expect(request(server.url, { timeout: 1000 })).resolves.toMatchObject({ ok: true }) + await server.close() + }) + + it('`throwHttpErrors` should not throw for non-2xx status codes when `false`', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.status(404) + response.end() + }) + + const request = createInstance() + const response = await request(server.url, { throwHttpErrors: false }) + + expect(response.ok).toBe(false) + expect(response.status).toBe(404) + expect(response.statusText).toBe('Not Found') + await server.close() + }) + + it('`throwHttpErrors` should throw for non-2xx status codes by default', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.status(404) + response.end() + }) + + const request = createInstance() + + await expect(request(server.url)).rejects.toThrowError('Not Found') + await server.close() + }) + + it("`json` should automatically get stringify'd and include proper request headers", async () => { + const server = await createTestServer() + server.post('/', (_request, response) => { + response.end() + }) + + let instance: any + const request = createInstance({ + afterResponse: [ + (request, options, response) => { + instance = { request, options, response } + } + ] + }) + + await expect(request(server.url, { method: 'post', json: { foo: true } })).resolves.toMatchObject({ ok: true }) + expect(instance.options.body).toBe(JSON.stringify({ foo: true })) + expect(instance.request.headers.get('content-type')).toBe('application/json') + await server.close() + }) + + it('`searchParams` should support URLSearchParams to produce the request url', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let instance: any + const request = createInstance({ + afterResponse: [ + (request, options, response) => { + instance = { request, options, response } + } + ] + }) + + const response = await request(server.url, { searchParams: new URLSearchParams({ foo: '1', bar: 'true' }) }) + + expect(response.ok).toBe(true) + expect(instance.request.url).toBe(`${server.url}/?foo=1&bar=true`) + await server.close() + }) + + it('`searchParams` should support a plain object', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let instance: any + const request = createInstance({ + afterResponse: [ + (request, options, response) => { + instance = { request, options, response } + } + ] + }) + + const response = await request(server.url, { searchParams: { foo: 1, bar: true } }) + + expect(response.ok).toBe(true) + expect(instance.request.url).toBe(`${server.url}/?foo=1&bar=true`) + await server.close() + }) + + it('`beforeRequest` hooks should run before a request is made', async () => { + const server = await createTestServer() + let requestReceived = false + server.get('/', (_request, response) => { + requestReceived = true + response.end() + }) + + const request = createInstance({ + beforeRequest: [ + () => { + expect(requestReceived).toBe(false) + } + ] + }) + + const response = await request(server.url) + expect(response.ok).toBe(true) + expect(requestReceived).toBe(true) + await server.close() + }) + + it('`beforeRequest` hooks should be able to modify the request options', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + let instance: any + const request = createInstance({ + beforeRequest: [ + () => { + return { + headers: { + 'x-test': 'test' + } + } + } + ], + afterResponse: [ + (request) => { + instance = { request } + } + ] + }) + + const response = await request(server.url) + expect(response.ok).toBe(true) + expect(instance.request.headers.get('x-test')).toBe('test') + await server.close() + }) + + it('`afterResponse` hooks should run after a response is received', async () => { + const server = await createTestServer() + let called = false + server.get('/', (_request, response) => { + called = true + response.end() + }) + + const request = createInstance({ + afterResponse: [ + (_request, _options, response) => { + expect(called).toBe(true) + expect(response).toBeDefined() + } + ] + }) + + const response = await request(server.url) + expect(response.ok).toBe(true) + await server.close() + }) + + it('`afterResponse` hooks should be able to modify the response object', async () => { + const server = await createTestServer() + server.get('/', (_request, response) => { + response.end() + }) + + const request = createInstance({ + afterResponse: [ + () => { + return new Response(new URLSearchParams({ hello: 'world' }), { status: 201 }) + } + ] + }) + + const response = await request(server.url) + expect(response.ok).toBe(true) + expect(response.status).toBe(201) + expect(await response.text()).toBe('hello=world') + await server.close() + }) +}) diff --git a/packages/core/src/create-request-client.ts b/packages/core/src/create-request-client.ts new file mode 100644 index 0000000000..d769155cde --- /dev/null +++ b/packages/core/src/create-request-client.ts @@ -0,0 +1,37 @@ +import addBasicAuthHeader from './middleware/before-request/add-basic-auth-header' +import prepareHeaders from './middleware/after-response/prepare-headers' +import prepareResponse from './middleware/after-response/prepare-response' +import createInstance, { AllRequestOptions, RequestOptions } from './request-client' +import type { ModifiedResponse } from './types' + +const baseClient = createInstance({ + timeout: 10000, + headers: { + 'user-agent': 'Segment' + }, + beforeRequest: [ + // Automatically handle username/password -> basic auth header + addBasicAuthHeader + ], + afterResponse: [prepareResponse, prepareHeaders] +}) + +export type RequestClient = ReturnType + +/** + * Creates a request client instance with Segment's default configuration + custom options + */ +export default function createRequestClient(...requestOptions: AllRequestOptions[]) { + let client = baseClient + + // TODO include `data` bundle in before/after hooks + // TODO expose before/after hooks to destination definition and action definition? + for (const options of requestOptions ?? []) { + client = client.extend(options) + } + + // Limit request client interface and handle basic auth scheme + return (url: string, options?: RequestOptions) => { + return client>(url, options) + } +} diff --git a/packages/core/src/create-test-event.ts b/packages/core/src/create-test-event.ts new file mode 100644 index 0000000000..7599a3fec3 --- /dev/null +++ b/packages/core/src/create-test-event.ts @@ -0,0 +1,43 @@ +import { v4 as uuidv4 } from '@lukeed/uuid' +import type { SegmentEvent } from './segment-event' + +export function createTestEvent(event: Partial = {}): SegmentEvent { + return { + anonymousId: uuidv4(), + context: { + ip: '8.8.8.8', + library: { + name: 'analytics.js', + version: '2.11.1' + }, + locale: 'en-US', + location: { + city: 'San Francisco', + country: 'United States', + latitude: 40.2964197, + longitude: -76.9411617, + speed: 0 + }, + page: { + path: '/academy/', + referrer: '', + search: '', + title: 'Analytics Academy', + url: 'https://segment.com/academy/' + }, + timezone: 'Europe/Amsterdam', + userAgent: + 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1' + }, + event: 'Test Event', + messageId: uuidv4(), + properties: {}, + receivedAt: new Date().toISOString(), + sentAt: new Date().toISOString(), + timestamp: new Date().toISOString(), + traits: {}, + type: 'track', + userId: 'user1234', + ...event + } +} diff --git a/packages/core/src/create-test-integration.ts b/packages/core/src/create-test-integration.ts new file mode 100644 index 0000000000..97de6424cb --- /dev/null +++ b/packages/core/src/create-test-integration.ts @@ -0,0 +1,68 @@ +import { createTestEvent } from './create-test-event' +import { Destination } from './destination-kit' +import { mapValues } from './map-values' +import type { DestinationDefinition } from './destination-kit' +import type { JSONObject } from './json-object' +import type { SegmentEvent } from './segment-event' + +interface InputData { + /** + * The Segment event. You can use `createTestEvent` if you want + * to construct an event from partial data. + */ + event?: Partial + /** + * The raw input - this is what customers define. It may include + * literal values as well as mapping-kit directives. + */ + mapping?: JSONObject + /** + * The settings for a destination instance. Includes things like + * `apiKey` or `subdomain`. Any fields that are used across all actions + * in a destination. + */ + settings?: Settings + /** + * Whether or not to use default mappings in the test. + * Set to `false` or omit if you want to explicitly provide the raw input. + * Set to `true` if you want to test the defaultMappings (along with any mapping passed in) + */ + useDefaultMappings?: boolean +} + +class TestDestination extends Destination { + responses: Destination['responses'] = [] + + constructor(destination: DestinationDefinition) { + super(destination) + } + + /** Testing method that runs an action e2e while allowing slightly more flexible inputs */ + async testAction( + action: string, + { event, mapping, settings, useDefaultMappings }: InputData + ): Promise { + mapping = mapping ?? {} + + if (useDefaultMappings) { + const fields = this.definition.actions[action].fields + const defaultMappings = mapValues(fields, 'default') + mapping = { ...defaultMappings, ...mapping } as JSONObject + } + + await super.executeAction(action, { + event: createTestEvent(event), + mapping, + settings: settings ?? ({} as T) + }) + + const responses = this.responses + this.responses = [] + + return responses + } +} + +export function createTestIntegration(destination: DestinationDefinition): TestDestination { + return new TestDestination(destination) +} diff --git a/packages/core/src/defaults.ts b/packages/core/src/defaults.ts new file mode 100644 index 0000000000..f94f3a3f26 --- /dev/null +++ b/packages/core/src/defaults.ts @@ -0,0 +1,14 @@ +import type { JSONSchema4, JSONSchema4Type } from 'json-schema' + +export function defaultValues(fields: Record) { + const obj: Record = {} + + for (const field of Object.keys(fields)) { + const defaultValue = (fields[field] as JSONSchema4).default + if (typeof defaultValue !== 'undefined') { + obj[field] = defaultValue + } + } + + return obj +} diff --git a/packages/core/src/destination-kit/README.md b/packages/core/src/destination-kit/README.md new file mode 100644 index 0000000000..b82fae0fa1 --- /dev/null +++ b/packages/core/src/destination-kit/README.md @@ -0,0 +1,198 @@ +# Destination Kit + + + + + +- [Overview](#overview) +- [Destination Definition](#destination-definition) +- [Action Definition](#action-definition) + - [cachedFields](#cachedfields) + - [perform](#perform) + + + +## Overview + +Destination Kit is an experimental interface for building destinations that are composed of +discrete actions that users want to perform when using an destination (e.g. "create or update +company", "track user", "trigger campaign"). + +```js +// Create or update a customer record in Customer.io +export default { + fields: { + id: { ... }, + custom_attributes: { ... }, + created_at: { ... }, + // ... more + }, + perform: (request, { payload, settings }) => { + const { id, custom_attributes: customAttrs, created_at, ...body } = payload + + return request(`https://example.com/customers/${id}`, { + method: 'put', + json: { ...customAttrs, ...body } + }) + } +} +``` + +The goals of Destination Kit are to minimize the amount of work it takes to build a destination (to +make them easy to build) and to standardize the most common patterns of destinations (to make them +easy to build correctly). Through this standard definition and dependency injection, we can use the same destination code to generate multiple things: + +- JSON Schema validation + +- Lambda functions to handle transformation and delivery of events. + +- Documentation that outlines what a destination can do, what information it needs to perform each + action, and how the destination behaves. + +- Centrifuge GX job configuration to move logic and work out of Lambda piecemeal. + +## Destination Definition + +A Destination definition is the entrypoint for a destination. It holds the configuration for how a destination should be presented to customers, and how it sends incoming events to partner APIs via actions. + +The definition of a `Destination` will look something like this, and should be the default export from your `destinations//index.ts`: + +```ts +const destination: DestinationDefinition = { + // The human-readable name of the destination + name: 'Your Destination Name', + + // The authentication scheme and fields + authentication: {}, + + // Extends the instance of the `fetch` client with request options + extendRequest: ({ settings }) => {}, + + // See "Actions" section below + actions: {} +} +``` + +#### extendRequest(function(Data)) + +extendRequest() adds a callback function that can set default +`fetch` request options for all requests made by actions +registered with this destination. It returns the base destination object. + +```ts +const destination: DestinationDefinition = { + name: 'Authorization Header Example', + + extendRequest({ settings }) { + return { + headers: { + Authorization: `Bearer ${settings.apiKey}` + } + } + } +} +``` + +## Action Definition + +Actions are the discrete units that represent an interaction with the partner API. +An action is composed of a sequence of steps that are created based on the definition, +like mapping the event to a payload defined in the action, validating that payload, and +performing the action (aka talking to the partner API). Actions will look like this: + +```ts +const action: ActionDefinition = { + // The action-specific fields that can be configured by the customer + // Ideally these fields will match what the partner API expects + fields: {}, + + // The set of fields that support UI-triggered interaction with the partner API to fetch choices (using the authenticated account) + // For example: fetching a list of Slack channels the user can select + dynamicFields: {} + + // The set of fields that should be dynamically fetched based on the mapped payload + // prior to executing the `perform` function. + // These fields will be made available via `cachedFields` + cachedFields: {} + + // The operation that an action performs when executed to send the mapped payload to the partner API + // This is the core function of an action. + perform: (request, data) => {} +} +``` + +#### cachedFields + +cachedRequest() wraps an external HTTP request with a cache. This is useful for reducing the number +of GET requests made for common operations like generating access tokens or determining if a user +exists yet in the partner API. + +Some notes on the cache implementation: + +- cachedRequest() does not cache negative values (null, undefined) by default to avoid common errors + in the most common use cases like caching access tokens or determining if a user needs to be + created or updated in the partner API. Caching of negative values can be turned on using the + `negative` option (see below). + +- The backing cache is not shared among cachedRequest() calls. Every cachedRequest() block has its + own cache. + +- The cache holds a maximum of 1,000 keys currently. This could be expanded in the future if needs dictate. + +The config object accepts the following fields (all fields are required unless otherwise noted): + +| Field | Type | Description | +| ---------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ttl` | `number` | Time, in seconds, that values are cached before they are expunged. E.g. `60` = 1 minute | +| `key` | `function(Data)` | A callback function that receives the [Data](#the-data-object) object and should return a unique string that identifies the object fetched by the `value` callback for the given payload. | +| `value` | `function(RequestClient, Data)` | A callback function that receives the `fetch`-based request client and the [Data](#the-data-object) object and returns the value that should be associated with the key. | +| `negative` | `boolean` | (Optional) Set this to `true` to cache negative values (null, undefined). | + +```ts +const action = { + // ... + + cachedFields: { + userEmail: { + ttl: 60, // 1 minute + key: ({ payload }) => payload.userId, + value: async (request, { payload }) => { + const resp = await request(`http://example.com/users/${payload.userId}`) + return resp.data + } + } + } +} +``` + +#### perform + +`perform()` accepts a callback function that receives a `fetch`-based request client and the [Data](#the-data-object) +object and returns the value that should be associated with the key. + +```ts +const action = { + // ... + + perform: (request, { payload, settings }) => { + return request(`http://example.com/users/${payload.userId}`, { + method: 'put', + headers: { + Authorization: `Bearer ${settings.apiKey}` + }, + json: payload.userProperties + }) + } +} +``` + +### The Data Object + +The Data object is an object passed to many of the callbacks that you'll define when adding steps +to an Action object. The Data object is used to propagate the incoming payload, settings, and +other values created at runtime among the various steps: + +| Field | Type | Description | +| ---------- | -------- | --------------------------------------------------- | +| `payload` | `object` | Incoming Segment event-mapped payload. | +| `settings` | `object` | Top-level destination setting values. E.g. `apiKey` | diff --git a/packages/core/src/destination-kit/action.ts b/packages/core/src/destination-kit/action.ts new file mode 100644 index 0000000000..d600105d62 --- /dev/null +++ b/packages/core/src/destination-kit/action.ts @@ -0,0 +1,369 @@ +// @ts-ignore no types +import { AggregateAjvError } from '@segment/ajv-human-errors' +import Ajv from 'ajv' +import dayjs from 'dayjs' +import { EventEmitter } from 'events' +import NodeCache from 'node-cache' +import createRequestClient from '../create-request-client' +import { get } from '../get' +import { JSONLikeObject, JSONObject } from '../json-object' +import { transform } from '../mapping-kit' +import { fieldsToJsonSchema } from './fields-to-jsonschema' +import { Response } from '../fetch' +import { ExecuteInput, Step, StepResult, Steps } from './step' +import type { ModifiedResponse } from '../types' +import type { DynamicFieldResponse, InputField, RequestExtension } from './types' + +type MaybePromise = T | Promise +type RequestClient = ReturnType + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type RequestFn = ( + request: RequestClient, + data: ExecuteInput +) => MaybePromise + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface ActionDefinition { + /** The unique identifier for the action, e.g. `postToChannel` */ + // key: string + + /** The display title of the action */ + title: string + + /** The display description of the action */ + description: string + + /** An optional fql query that will be used to prepopulate the action when it is first set up */ + defaultSubscription?: string + + /** Whether or not this action should be visible/configurable in the UI */ + hidden?: boolean + + /** + * The fields used to perform the action. These fields should match what the partner API expects. + */ + fields: Record + + /** + * A way to "register" dynamic fields. + * This is likely going to change as we productionalize the data model and definition object + */ + dynamicFields?: { + [K in keyof Payload]?: RequestFn + } + + /** + * Register fields that should be executed, cached and provided + * to the action's `perform` function + */ + cachedFields?: { + [field: string]: { + key: (data: ExecuteInput) => string + ttl: number + value: RequestFn + negative?: boolean + } + } + + /** The operation to perform when this action is triggered */ + perform: RequestFn +} + +class MapInput extends Step { + executeStep(data: ExecuteInput): Promise { + // Transforms the initial payload (event) + action settings (from `subscriptions[0].mapping`) + // into input data that the action can use to talk to partner apis + if (data.mapping) { + // Technically we can't know whether or not `transform` returns the exact shape of Payload here, hence the casting + // It will be validated in subsequent steps + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data.payload = transform(data.mapping, data.payload as any) as Payload + } + + return Promise.resolve('MapInput completed') + } +} + +const ajv = new Ajv({ + // Coerce types to be a bit more liberal. + coerceTypes: true, + // Return all validation errors, not just the first. + allErrors: true, + // Include reference to schema and data in error values. + verbose: true, + // Remove properties not defined the schema object + removeAdditional: true, + // Use a more parse-able format for JSON paths. + jsonPointers: true +}) + +// Extend with additional supported formats +ajv.addFormat('password', () => true) +ajv.addFormat('text', () => true) +ajv.addFormat('date-like', (data: string) => { + let date = dayjs(data) + + if (String(Number(data)) === data) { + // parse as unix + if (data.length === 13) { + date = dayjs(Number(data)) + } + + date = dayjs.unix(Number(data)) + } + + return date.isValid() +}) + +export class Validate extends Step { + field: ExecuteInputField + validate: Ajv.ValidateFunction + + constructor(field: ExecuteInputField, schema: object) { + super() + this.field = field + this.validate = ajv.compile(schema) + } + + executeStep(data: ExecuteInput): Promise { + if (!this.validate(data[this.field])) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + throw new AggregateAjvError(this.validate.errors) + } + + return Promise.resolve('Validate completed') + } +} + +/** + * Request handles delivering a payload to an external API. It uses the `fetch` API under the hood. + * + * The callback should be able to return the raw request instead of needing to do `return response.data` etc. + */ +class Request extends Step { + requestFn: RequestFn | undefined + extendRequest: RequestExtension | undefined + + constructor( + extendRequest: RequestExtension | undefined, + requestFn?: RequestFn + ) { + super() + this.extendRequest = extendRequest + this.requestFn = requestFn + } + + async executeStep(data: ExecuteInput): Promise { + if (!this.requestFn) { + return '' + } + + const requestClient = this.createRequestClient(data) + + const response = await this.requestFn(requestClient, data) + + /** + * Try to use the parsed response `.data` or `.content` string + * @see {@link ../middleware/after-response/prepare-response.ts} + */ + if (response instanceof Response) { + return ((response as ModifiedResponse).data as JSONObject) ?? (response as ModifiedResponse).content + } + + // otherwise, we don't really know what this is, so return as-is + return response + } + + protected createRequestClient(data: ExecuteInput): RequestClient { + // TODO turn `extendRequest` into a beforeRequest hook + const options = this.extendRequest?.(data) ?? {} + const client = createRequestClient(options, { + afterResponse: [ + // Keep track of the request(s) associated with a response + (request, options, response) => { + // TODO figure out the types here... + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const modifiedResponse: any = response + modifiedResponse.request = request + modifiedResponse.options = options + + this.emit('response', modifiedResponse) + + return modifiedResponse + } + ] + }) + return client + } +} + +interface CachedRequestConfig { + key: (data: ExecuteInput) => string + value: RequestFn + as: string + ttl: number + negative?: boolean +} + +// CachedRequest is like Request but cached. Next question. +class CachedRequest extends Request { + keyFn: Function + valueFn: Function + as: string + negative: boolean + cache: NodeCache + + constructor( + extension: RequestExtension | undefined, + config: CachedRequestConfig + ) { + super(extension) + + this.keyFn = config.key + this.valueFn = config.value + this.as = config.as + this.negative = config.negative || false + + this.cache = new NodeCache({ + stdTTL: config.ttl, + maxKeys: 1000 + }) + } + + async executeStep(data: ExecuteInput): Promise { + const k = this.keyFn(data) + let v = this.cache.get(k) + + if (v !== undefined) { + data.cachedFields[this.as] = v + return 'cache hit' + } + + const request = this.createRequestClient(data) + + try { + v = await this.valueFn(request, data) + } catch (e) { + if (get(e, 'response.status') === 404) { + v = undefined + } else { + throw e + } + } + + // Only cache if value is not negative *or* negative option is set. Negative caching is off by + // default because the common cases are: A) auth token generation, which should never be + // negative, and B) create-or-update patterns, where the resource should exist after the first + // negative value. + if ((v !== null && v !== undefined) || this.negative) { + this.cache.set(k, v) + } + + return 'cache miss' + } +} + +interface ExecuteDynamicFieldInput { + settings: Settings + payload: Payload + cachedFields: { [key: string]: string } + page?: string +} + +type ExecuteInputField = 'payload' | 'settings' + +/** + * Action is the beginning step for all partner actions. Entrypoints always start with the + * MapAndValidateInput step. + */ +export class Action extends EventEmitter { + readonly steps: Steps + private extendRequest: RequestExtension | undefined + private dynamicFieldCache: { [key: string]: RequestFn } + + constructor(definition: ActionDefinition, extendRequest?: RequestExtension) { + super() + + this.steps = new Steps() + const step = new MapInput() + this.steps.push(step) + + this.dynamicFieldCache = {} + + if (extendRequest) { + // This must come before we load the definition because + // it instantiates "steps" with whatever request extensions + // are defined at that moment in time + this.extendRequest = extendRequest + } + + this.loadDefinition(definition) + } + + async execute(data: ExecuteInput): Promise { + const results = await this.steps.execute(data) + + const finalResult = results[results.length - 1] + if (finalResult.error) { + throw finalResult.error + } + + return results + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + executeDynamicField(field: string, data: ExecuteDynamicFieldInput): any { + if (!this.dynamicFieldCache[field]) { + return { + data: [], + pagination: {} + } + } + + const step = new Request(this.extendRequest, this.dynamicFieldCache[field]) + + return step.executeStep(data) + } + + private loadDefinition(definition: ActionDefinition): void { + if (definition.fields) { + this.validatePayload(definition.fields) + } + + Object.entries(definition.dynamicFields ?? {}).forEach(([field, callback]) => { + this.dynamicField(field, callback as RequestFn) + }) + + Object.entries(definition.cachedFields ?? {}).forEach(([field, cacheConfig]) => { + this.cachedRequest({ + ...cacheConfig, + as: field + }) + }) + + if (definition.perform) { + this.request(definition.perform) + } + } + + private validatePayload(fields: Record): void { + const step = new Validate('payload', fieldsToJsonSchema(fields)) + this.steps.push(step) + } + + private dynamicField(field: string, callback: RequestFn): void { + this.dynamicFieldCache[field] = callback + } + + private request(requestFn: RequestFn): void { + const step = new Request(this.extendRequest, requestFn) + step.on('response', (response) => this.emit('response', response)) + this.steps.push(step) + } + + private cachedRequest(config: CachedRequestConfig): void { + const step = new CachedRequest(this.extendRequest, config) + this.steps.push(step) + } +} diff --git a/packages/core/src/destination-kit/fields-to-jsonschema.ts b/packages/core/src/destination-kit/fields-to-jsonschema.ts new file mode 100644 index 0000000000..c9d7e7e254 --- /dev/null +++ b/packages/core/src/destination-kit/fields-to-jsonschema.ts @@ -0,0 +1,85 @@ +import { JSONSchema4, JSONSchema4Type, JSONSchema4TypeName } from 'json-schema' +import type { InputField, FieldTypeName } from './types' + +function toJsonSchemaType(type: FieldTypeName): JSONSchema4TypeName | JSONSchema4TypeName[] { + switch (type) { + case 'string': + case 'text': + case 'password': + return 'string' + case 'datetime': + return ['string', 'number'] + default: + return type + } +} + +export function fieldsToJsonSchema(fields: Record = {}): JSONSchema4 { + const required: string[] = [] + const properties: Record = {} + + for (const [key, field] of Object.entries(fields)) { + const schemaType = toJsonSchemaType(field.type) + + let schema: JSONSchema4 = { + title: field.label, + description: field.description, + type: schemaType, + format: field.format, + default: field.default as JSONSchema4Type + } + + if (field.type === 'datetime') { + schema.format = 'date-like' + // Override generated types + schema.tsType = 'string | number' + } else if (field.type === 'password') { + schema.format = 'password' + } else if (field.type === 'text') { + schema.format = 'text' + } + + if (field.dynamic) { + schema.autocomplete = true + schema.dynamic = true + } + + if (field.allowNull) { + schema.type = ([] as JSONSchema4TypeName[]).concat(schemaType, 'null') + + if (typeof schema.tsType === 'string' && !schema.tsType.includes('null')) { + schema.tsType += ' | null' + } + } + + if (field.multiple) { + schema.items = { type: schemaType } + schema.type = 'array' + } + + // Note: this is used for the json schema validation and type-generation, + // but is not stored in the db. It only lives in the code. + if (schemaType === 'object' && field.properties) { + if (field.multiple) { + schema.items = fieldsToJsonSchema(field.properties) + } else { + schema = { ...schema, ...fieldsToJsonSchema(field.properties) } + } + } + + properties[key] = schema + + // Grab all the field keys with `required: true` + if (field.required) { + required.push(key) + } + } + + return { + $schema: 'http://json-schema.org/schema#', + type: 'object', + additionalProperties: false, + properties, + required + } +} diff --git a/packages/core/src/destination-kit/index.ts b/packages/core/src/destination-kit/index.ts new file mode 100644 index 0000000000..c203bc9fa0 --- /dev/null +++ b/packages/core/src/destination-kit/index.ts @@ -0,0 +1,356 @@ +import { validate, parseFql, ErrorCondition } from '@segment/destination-subscriptions' +import { JSONSchema4 } from 'json-schema' +import { Action, ActionDefinition, Validate, RequestFn } from './action' +import { ExecuteInput, StepResult } from './step' +import { time, duration } from '../time' +import { JSONLikeObject, JSONObject } from '../json-object' +import { SegmentEvent } from '../segment-event' +import { fieldsToJsonSchema } from './fields-to-jsonschema' +import createRequestClient, { RequestClient } from '../create-request-client' +import type { ModifiedResponse } from '../types' +import type { InputField, RequestExtension } from './types' +import type { AllRequestOptions } from '../request-client' +import { IntegrationError } from '../errors' + +export type { ActionDefinition, ExecuteInput, RequestFn } +export { fieldsToJsonSchema } + +export interface SubscriptionStats { + duration: number + destination: string + action: string + subscribe: string + state: string + input: JSONLikeObject + output: StepResult[] | null +} + +interface PartnerActions { + [key: string]: Action +} + +export interface DestinationDefinition { + /** The name of the destination */ + name: string + /** A human-friendly description of the destination */ + description?: string + /** + * The url-friendly unique slug for the destination + * When provided, the `register` command will use this slug + * instead of generating one from the `name` + */ + slug?: string + /** An optional function to extend requests sent from the destination (including all actions) */ + extendRequest?: RequestExtension + /** Optional authentication configuration */ + authentication?: AuthenticationScheme + /** Actions */ + actions: { + [key: string]: ActionDefinition + } + /** Subscription presets automatically applied in quick setup */ + presets?: Subscription[] +} + +export interface Subscription { + name?: string + partnerAction: string + subscribe: string + mapping?: JSONObject +} + +export interface OAuth2ClientCredentials { + /** Publicly exposed string that is used by the partner API to identify the application, also used to build authorization URLs that are presented to users */ + clientId: string + /** Used to authenticate the identity of the application to the partner API when the application requests to access a user’s account, must be kept private between the application and the API. */ + clientSecret: string +} + +export interface RefreshAccessTokenResult { + /** OAuth2 access token that was recently acquired */ + accessToken: string + /** Provide in case the partner API also updates the refresh token when requesting a fresh access token */ + refreshToken?: string +} + +interface AuthSettings { + settings: Settings +} + +interface Authentication { + /** The authentication scheme */ + scheme: 'basic' | 'custom' | 'oauth2' + /** The fields related to authentication */ + fields: Record + /** A function that validates the user's authentication inputs */ + testAuthentication: (request: RequestClient, input: AuthSettings) => Promise | unknown +} + +/** + * Custom authentication scheme + * Typically used for "API Key" authentication. + */ +export interface CustomAuthentication extends Authentication { + scheme: 'custom' +} + +/** + * Basic authentication scheme + * @see {@link https://datatracker.ietf.org/doc/html/rfc7617} + */ +export interface BasicAuthentication extends Authentication { + scheme: 'basic' +} + +/** + * OAuth2 authentication scheme + */ +export interface OAuth2Authentication extends Authentication { + scheme: 'oauth2' + /** A function that is used to refresh the access token + * @todo look into merging input and oauthConfig so we can keep all the request functions with the same method signature (2 arguments) + */ + refreshAccessToken?: ( + request: RequestClient, + input: AuthSettings, + oauthConfig: OAuth2ClientCredentials + ) => Promise | undefined +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AuthenticationScheme = + | BasicAuthentication + | CustomAuthentication + | OAuth2Authentication + +interface EventInput { + readonly event: SegmentEvent + readonly mapping: JSONObject + readonly settings: Settings +} + +export interface DecoratedResponse extends ModifiedResponse { + request: Request + options: AllRequestOptions +} + +export class Destination { + readonly definition: DestinationDefinition + readonly name: string + readonly authentication?: AuthenticationScheme + readonly extendRequest?: RequestExtension + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readonly actions: PartnerActions + readonly responses: DecoratedResponse[] + readonly settingsSchema?: JSONSchema4 + + constructor(destination: DestinationDefinition) { + this.definition = destination + this.name = destination.name + this.extendRequest = destination.extendRequest + this.actions = {} + this.authentication = destination.authentication + this.responses = [] + + // Convert to complete JSON Schema + if (this.authentication?.fields) { + this.settingsSchema = fieldsToJsonSchema(this.authentication.fields) + } + + for (const action of Object.keys(destination.actions)) { + this.partnerAction(action, destination.actions[action]) + } + } + + async testAuthentication(settings: Settings): Promise { + const context: ExecuteInput = { settings, payload: {}, cachedFields: {} } + + if (this.settingsSchema) { + const step = new Validate('settings', this.settingsSchema) + await step.executeStep(context) + } + + if (!this.authentication?.testAuthentication) { + return + } + + const options = this.extendRequest?.(context) ?? {} + const requestClient = createRequestClient(options) + + try { + await this.authentication.testAuthentication(requestClient, { settings }) + } catch (error) { + throw new Error('Credentials are invalid') + } + } + + refreshAccessToken( + settings: Settings, + oauthClientCredentials: OAuth2ClientCredentials + ): Promise | undefined { + if (this.authentication?.scheme !== 'oauth2') { + throw new IntegrationError( + 'refreshAccessToken is only valid with oauth2 authentication scheme', + 'NotImplemented', + 501 + ) + } + // TODO: clean up context/extendRequest so we don't have to send information that is not needed (payload & cachedFields) + const context: ExecuteInput = { settings, payload: {}, cachedFields: {} } + const options = this.extendRequest?.(context) ?? {} + const requestClient = createRequestClient(options) + + if (!this.authentication?.refreshAccessToken) { + return undefined + } + + return this.authentication.refreshAccessToken(requestClient, { settings }, oauthClientCredentials) + } + + private partnerAction(slug: string, definition: ActionDefinition): Destination { + const action = new Action(definition, this.extendRequest) + + action.on('response', (response) => { + if (response) { + this.responses.push(response) + } + }) + + this.actions[slug] = action + + return this + } + + protected executeAction( + actionSlug: string, + { event, mapping, settings }: EventInput + ): Promise { + const action = this.actions[actionSlug] + if (!action) { + return Promise.resolve([]) + } + + return action.execute({ + cachedFields: {}, + mapping, + payload: event, + settings + }) + } + + private async onSubscription( + subscription: Subscription, + event: SegmentEvent, + settings: Settings, + onComplete?: (stats: SubscriptionStats) => void + ): Promise { + const subscriptionStartedAt = time() + const actionSlug = subscription.partnerAction + const input = { + event, + mapping: subscription.mapping || {}, + settings + } + + let state = 'pending' + let results: StepResult[] | null = null + + try { + if (!subscription.subscribe || typeof subscription.subscribe !== 'string') { + state = 'skipped' + results = [{ output: 'invalid subscription' }] + return results + } + + const parsedSubscription = parseFql(subscription.subscribe) + + if ((parsedSubscription as ErrorCondition).error) { + state = 'skipped' + results = [{ output: `invalid subscription : ${(parsedSubscription as ErrorCondition).error.message}` }] + return results + } + + const isSubscribed = validate(parsedSubscription, event) + if (!isSubscribed) { + state = 'skipped' + results = [{ output: 'not subscribed' }] + return results + } + + results = await this.executeAction(actionSlug, input) + state = 'done' + + return results + } catch (error) { + state = 'errored' + results = [{ error }] + + if (error.name === 'AggregateAjvError' || error.name === 'ValidationError') { + error.status = 400 + } + + throw error + } finally { + const subscriptionEndedAt = time() + const subscriptionDuration = duration(subscriptionStartedAt, subscriptionEndedAt) + + onComplete?.({ + duration: subscriptionDuration, + destination: this.name, + action: actionSlug, + subscribe: subscription.subscribe, + state, + input: { + event: input.event as unknown as JSONLikeObject, + mapping: input.mapping, + settings: input.settings as unknown as JSONLikeObject + }, + output: results + }) + } + } + + /** + * Note: Until we move subscriptions upstream (into int-consumer) we've opted + * to have failures abort the set of subscriptions and get potentially retried by centrifuge + */ + public async onEvent( + event: SegmentEvent, + settings: JSONObject, + onComplete?: (stats: SubscriptionStats) => void + ): Promise { + const subscriptions = this.getSubscriptions(settings) + const destinationSettings = this.getDestinationSettings(settings) + + const promises = subscriptions.map((subscription) => + this.onSubscription(subscription, event, destinationSettings, onComplete) + ) + + const results = await Promise.all(promises) + + return ([] as StepResult[]).concat(...results) + } + + private getSubscriptions(settings: JSONObject): Subscription[] { + const { subscription, subscriptions } = settings + let parsedSubscriptions + + // To support event tester we need to parse and validate multiple subscriptions from the settings + if (subscription) { + parsedSubscriptions = [subscription] + } else if (typeof subscriptions === 'string') { + parsedSubscriptions = JSON.parse(subscriptions) + } else if (Array.isArray(subscriptions)) { + parsedSubscriptions = subscriptions + } else { + parsedSubscriptions = [] + } + + return parsedSubscriptions as Subscription[] + } + + private getDestinationSettings(settings: JSONObject): Settings { + const { subcription, subscriptions, ...otherSettings } = settings + return otherSettings as unknown as Settings + } +} diff --git a/packages/core/src/destination-kit/step.ts b/packages/core/src/destination-kit/step.ts new file mode 100644 index 0000000000..89fc20a47c --- /dev/null +++ b/packages/core/src/destination-kit/step.ts @@ -0,0 +1,82 @@ +import { EventEmitter } from 'events' +import { JSONObject } from '../json-object' + +export interface StepResult { + output?: JSONObject | string | null | undefined + error?: JSONObject | null +} + +export interface ExecuteInput { + /** The subscription mapping definition */ + readonly mapping?: JSONObject + /** The global destination settings */ + readonly settings: Settings + /** The transformed input data, based on `mapping` + `event` */ + payload: Payload + /** The ids from cached requests */ + cachedFields: { [key: string]: string | null | undefined } + /** The page used in dynamic field requests */ + page?: string +} + +/** + * Step is the base class for all discrete execution steps. It handles executing the step, logging, + * catching errors, and returning a result object. + */ +export class Step extends EventEmitter { + executeStep?(data: ExecuteInput): Promise + + async execute(data: ExecuteInput): Promise { + const result: StepResult = { + output: null, + error: null + } + + if (!this.executeStep) { + return result + } + + try { + result.output = await this.executeStep(data) + } catch (e) { + result.error = e + } + + return result + } +} + +/** + * Steps is a list of one or more Step instances that can be executed in-order. + */ +export class Steps { + steps: Step[] + + constructor() { + this.steps = [] + } + + push(step: Step): void { + this.steps.push(step) + } + + async execute(data: ExecuteInput): Promise { + if (this.steps.length === 0) { + throw new Error('no steps defined') + } + + const results: StepResult[] = [] + + for (const step of this.steps) { + const result = await step.execute(data) + + results.push(result) + + if (result.error) { + break + } + } + + return results + } +} diff --git a/packages/core/src/destination-kit/types.ts b/packages/core/src/destination-kit/types.ts new file mode 100644 index 0000000000..fb99d2b27f --- /dev/null +++ b/packages/core/src/destination-kit/types.ts @@ -0,0 +1,93 @@ +import type { RequestOptions } from '../request-client' +import type { ExecuteInput } from './step' + +export interface DynamicFieldResponse { + body: { + data: DynamicFieldItem[] + pagination: { + nextPage?: string + } + } +} + +export interface DynamicFieldItem { + label: string + value: string +} + +/** The supported field type names */ +export type FieldTypeName = 'string' | 'text' | 'number' | 'integer' | 'datetime' | 'boolean' | 'password' | 'object' + +/** The shape of an input field definition */ +export interface InputField { + /** A short, human-friendly label for the field */ + label: string + /** A human-friendly description of the field */ + description: string + /** The data type for the field */ + type: FieldTypeName + /** Whether null is allowed or not */ + allowNull?: boolean + /** Whether or not the field accepts multiple values (an array of `type`) */ + multiple?: boolean + /** An optional default value for the field */ + default?: FieldValue + /** A placeholder display value that suggests what to input */ + placeholder?: string + /** Whether or not the field supports dynamically fetching options */ + dynamic?: boolean + /** Whether or not the field is required */ + required?: boolean + /** + * Optional definition for the properties of `type: 'object'` fields + * (also arrays of objects when using `multiple: true`) + * Note: this part of the schema is not persisted outside the code + * but is used for validation and typedefs + */ + properties?: Record + /** + * Format option to specify more nuanced 'string' types + * @see {@link https://github.com/ajv-validator/ajv/tree/v6#formats} + */ + format?: + | 'date' // full-date according to RFC3339. + | 'time' // time with optional time-zone. + | 'date-time' // date-time from the same source (time-zone is mandatory). date, time and date-time validate ranges in full mode and only regexp in fast mode (see options). + | 'uri' // full URI. + | 'uri-reference' // URI reference, including full and relative URIs. + | 'uri-template' // URI template according to RFC6570 + | 'email' // email address. + | 'hostname' // host name according to RFC1034. + | 'ipv4' // IP address v4. + | 'ipv6' // IP address v6. + | 'regex' // tests whether a string is a valid regular expression by passing it to RegExp constructor. + | 'uuid' // Universally Unique IDentifier according to RFC4122. + | 'password' // hint to the UI to hide/obfuscate input strings + | 'text' // longer strings +} + +export type FieldValue = string | number | boolean | object | Directive + +export interface IfDirective { + '@if': { + exists?: FieldValue + then: FieldValue + else?: FieldValue + } +} + +export interface TemplateDirective { + '@template': string +} + +export interface PathDirective { + '@path': string +} + +export type Directive = IfDirective | TemplateDirective | PathDirective + +/** + * A function to configure a request client instance with options + * that will be applied to every request made by that instance + */ +export type RequestExtension = (data: ExecuteInput) => RequestOptions diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts new file mode 100644 index 0000000000..2312665443 --- /dev/null +++ b/packages/core/src/errors.ts @@ -0,0 +1,70 @@ +import { CustomError } from 'ts-custom-error' + +/** + * Error due to generic misconfiguration of user settings. + * Should include a user-friendly message, and optionally an error reason and status code. + * - 4xx errors are not automatically retried, except for 408, 423, 429 + * - 5xx are automatically retried, except for 501 + */ +export class IntegrationError extends CustomError { + code: string | undefined + status: number | undefined + + /** + * @param message - a human-friendly message to display to users + * @param code - an optional error code/reason + * @param status - an optional http status code (e.g. 400) + */ + constructor(message = '', code?: string, status?: number) { + super(message) + this.status = status + this.code = code + } +} + +type RetryableStatusCodes = + | 408 + | 423 + | 429 + | 500 + | 502 + | 503 + | 504 + | 505 + | 506 + | 507 + | 508 + | 509 + | 510 + | 511 + | 598 + | 599 + +/** + * Error that should halt execution but allows the request to be retried automatically. + * This error signals to Segment that a transient error occurred, and retrying the request may succeed without user intervention. + */ +export class RetryableError extends CustomError { + status: RetryableStatusCodes + + constructor(message = '', status: RetryableStatusCodes = 500) { + super(message) + this.status = status + } +} + +/** + * Error for when a user's authentication is not valid. + * This could happen when a token or API key has expired or been revoked, + * or various other scenarios where the authentication credentials are no longer valid. + * + * This error signals to Segment that the user must manually fix their credentials for events to succeed. + */ +export class InvalidAuthenticationError extends CustomError { + status = 401 + code = 'invalid_authentication' + + constructor(message = '') { + super(message) + } +} diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts new file mode 100644 index 0000000000..fbac0663a5 --- /dev/null +++ b/packages/core/src/fetch.ts @@ -0,0 +1,5 @@ +// import fetch ponyfill +import fetch, { Headers, Request, Response } from 'cross-fetch' + +export { Headers, Request, Response } +export default fetch diff --git a/packages/core/src/get.ts b/packages/core/src/get.ts new file mode 100644 index 0000000000..1d94a566db --- /dev/null +++ b/packages/core/src/get.ts @@ -0,0 +1,21 @@ +/** + * Lightweight alternative to lodash.get with similar coverage + * Supports basic path lookup via dot notation `'foo.bar[0].baz'` or an array ['foo', 'bar', '0', 'baz'] + */ +export function get( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + obj: any, + path: string | string[], + defValue?: Default +): T | Default | undefined { + // If path is not defined or it has false value + if (!path) return defValue + + // Check if path is string or array. Regex : ensure that we do not have '.' and brackets. + // Regex explained: https://regexr.com/58j0k + const pathArray = Array.isArray(path) ? path : (path.match(/([^[.\]])+/g) as string[]) + + // Find value if exist return otherwise return undefined value + const value = pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj) + return typeof value !== 'undefined' ? value : defValue +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000000..a967c64c96 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,37 @@ +export { Destination, fieldsToJsonSchema } from './destination-kit' +export { transform } from './mapping-kit' +export { createTestEvent } from './create-test-event' +export { createTestIntegration } from './create-test-integration' +export { defaultValues } from './defaults' +export { IntegrationError, InvalidAuthenticationError, RetryableError } from './errors' +export { get } from './get' +export { omit } from './omit' +export { removeUndefined } from './remove-undefined' +export { time, duration } from './time' + +export { realTypeOf, isObject, isArray, isString } from './real-type-of' + +export type { RequestOptions } from './request-client' +export { default as fetch, Request, Response, Headers } from './fetch' + +export type { + ActionDefinition, + DestinationDefinition, + ExecuteInput, + Subscription, + SubscriptionStats, + AuthenticationScheme, + BasicAuthentication, + CustomAuthentication, + OAuth2Authentication, + OAuth2ClientCredentials, + RefreshAccessTokenResult, + RequestFn, + DecoratedResponse +} from './destination-kit' + +export type { DynamicFieldResponse, DynamicFieldItem, InputField, RequestExtension } from './destination-kit/types' + +export type { JSONPrimitive, JSONValue, JSONObject, JSONArray, JSONLike, JSONLikeObject } from './json-object' + +export type { SegmentEvent } from './segment-event' diff --git a/packages/core/src/json-object.ts b/packages/core/src/json-object.ts new file mode 100644 index 0000000000..99e49bc0be --- /dev/null +++ b/packages/core/src/json-object.ts @@ -0,0 +1,11 @@ +export type JSONPrimitive = string | number | boolean | null +export type JSONValue = JSONPrimitive | JSONObject | JSONArray +export type JSONObject = { [member: string]: JSONValue } +export type JSONArray = Array + +// If you need to also support `undefined`, though it would get dropped during serialization +export type JSONLike = JSONPrimitive | JSONLikeObject | Array | Date | undefined +export type JSONLikeObject = { + [member: string]: JSONLike + [member: number]: JSONLike +} diff --git a/packages/core/src/map-values.ts b/packages/core/src/map-values.ts new file mode 100644 index 0000000000..c529f1262c --- /dev/null +++ b/packages/core/src/map-values.ts @@ -0,0 +1,16 @@ +/** + * Lightweight alternative to lodash.mapValues + * Creates an object with the same keys as the input object + */ +export function mapValues< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Values extends Record, + Obj extends Record, + ValueKey extends keyof Values +>(obj: Obj, key: ValueKey): { [K in keyof Obj]: Obj[K][ValueKey] } { + return Object.entries(obj).reduce((agg, [name, value]) => { + // @ts-ignore this is a pretty complex type, skipping for now + agg[name] = value[key] + return agg + }, {} as { [K in keyof Obj]: Obj[K][ValueKey] }) +} diff --git a/packages/core/src/mapping-kit/README.md b/packages/core/src/mapping-kit/README.md new file mode 100644 index 0000000000..397a42cadb --- /dev/null +++ b/packages/core/src/mapping-kit/README.md @@ -0,0 +1,433 @@ +# Mapping Kit + +Mapping Kit is a library for mapping and transforming JSON payloads. It exposes a function that +accepts a mapping configuration object and a payload object and outputs a mapped and transformed +payload. A mapping configuration is a mixture of raw values (values that appear in the output +payload as they appear in the mapping configuration) and directives, which can fetch and transform +data from the input payload. + +For example: + +```json +Mapping: + +{ + "name": "Mr. Rogers", + "neighborhood": { "@path": "$.properties.neighborhood" }, + "greeting": { "@template": "Won't you be my {{properties.noun}}?" } +} + +Input: + +{ + "type": "track", + "event": "Sweater On", + "context": { + "library": { + "name": "analytics.js", + "version": "2.11.1" + } + }, + "properties": { + "neighborhood": "Latrobe", + "noun": "neighbor", + "sweaterColor": "red" + } +} + +Output: + +{ + "name": "Mr. Rogers", + "neighborhood": "Latrobe", + "greeting": "Won't you be my neighbor?" +} +``` + +## Table of contents + + + + + +- [Usage](#usage) +- [Terms](#terms) +- [Mixing raw values and directives](#mixing-raw-values-and-directives) +- [Validation](#validation) +- [Options](#options) + - [merge](#merge) +- [Removing values from object](#removing-values-from-object) +- [Directives](#directives) + - [@if](#if) + - [@path](#path) + - [@template](#template) + + + +## Usage + +```ts +import { transform } from '../mapping-kit' + +const mapping = { '@path': '$.foo.bar' } +const input = { foo: { bar: 'Hello!' } } + +const output = transform(mapping, input) +// => "Hello!" +``` + +## Terms + +In Mapping Kit, there are only two kinds of values: **raw values** and **directives**. Raw values +can be any JSON value and Mapping Kit will return them in the output payload untouched: + +```json +42 + +"Hello, world!" + +{ "foo": "bar" } + +["product123", "product456"] +``` + +Directives are objects with a single @-prefixed key that tell Mapping Kit to fetch data from the +input payload or transform some data: + +```json +{ "@path": "$.properties.name" } + +{ "@template": "Hello there, {{traits.name}}" } +``` + +In this document, the act of converting a directive to its final raw value is called "resolving" the +directive. + +## Mixing raw values and directives + +Directives and raw values can be mixed to create complex mappings. For example: + +```json +Mapping: + +{ + "action": "create", + "userId": { + "@path": "$.traits.email" + }, + "userProperties": { + "@path": "$.traits" + } +} + +Input: + +{ + "traits": { + "name": "Peter Gibbons", + "email": "peter@example.com", + "plan": "premium", + "logins": 5, + "address": { + "street": "6th St", + "city": "San Francisco", + "state": "CA", + "postalCode": "94103", + "country": "USA" + } + } +} + + +Output: + +{ + "action": "create", + "userId": "peter@example.com", + "userProperties": { + "name": "Peter Gibbons", + "email": "peter@example.com", + "plan": "premium", + "logins": 5, + "address": { + "street": "6th St", + "city": "San Francisco", + "state": "CA", + "postalCode": "94103", + "country": "USA" + } + } +} +``` + +A directive may not, however, be mixed in at the same level as a raw value: + +```json +Invalid: + +{ + "foo": "bar", + "@path": "$.properties.biz" +} + +Valid: + +{ + "foo": "bar", + "baz": { "@path": "$.properties.biz" } +} +``` + +And a directive may only have one @-prefixed directive in it: + +```json +Invalid: + +{ + "@path": "$.foo.bar", + "@template": "{{biz.baz}" +} + +Valid: + +{ + "foo": { "@path": "$.foo.bar" }, + "baz": { + "@template": "{{biz.baz}}" + } +} +``` + +## Validation + +Mapping configurations can be validated using JSON Schema. The [test +suite][schema.test.js] is a good source-of-truth for current implementation behavior. + +[schema.test.js]: https://github.com/segmentio/action-destinations/blob/master/packages/destination-actions/src/lib/mapping-kit/__tests__ + +## Options + +Options can be passed to the `transform()` function as the third parameter: + +```js +const output = transform(mapping, input, options) +``` + +Available options: + +```js +{ + merge: true // default false +} +``` + +### merge + +If true, `merge` will cause the mapped value to be merged onto the input payload. This is useful +when you only want to map/transform a small number of fields: + +```json +Input: + +{ + "a": { + "b": 1 + }, + "c": 2 +} + +Options: + +{ + "merge": true +} + +Mappings: + +{} +=> +{ + "a": { + "b": 1 + }, + "c": 2 +} + +{ + "a": 3 +} +=> +{ + "a": 3, + "c": 2 +} + +{ + "a": { + "c": 3 + } +} +=> +{ + "a": { + "b": 1, + "c": 3 + }, + "c": 2 +} +``` + +## Removing values from object + +`undefined` values in objects are removed from the mapped output while `null` is not: + +```json +Input: + +{ + "a": 1 +} + +Mappings: + +{ + "foo": { + "@path": "$.a" + }, + "bar": { + "@path": "$.b" + }, + "baz": null +} +=> +{ + "foo": 1, + "baz": null +} +``` + +## Directives + +### @if + +The @if directive resolves to different values based on a given conditional. It must have at least +one conditional (see below) and one branch ("then" or "else"). + +The supported conditional values are: + +- "exists": If the given value is not undefined or null, the @if directive resolves to the "then" + value. Otherwise, the "else" value is used. + +```json +Input: + +{ + "a": "cool", + "b": true +} + +Mappings: + +{ + "@if": { + "exists": { "@path": "$.a" }, + "then": "yep", + "else": "nope" + } +} +=> +"yep" + +{ + "@if": { + "exists": { "@path": "$.nope" }, + "then": "yep", + "else": "nope" + } +} +=> +"nope" +``` + +If "then" or "else" are not defined and the conditional indicates that their value should be used, +the field will not appear in the resolved output. This is useful for including a field only if it +(or some other field) exists: + +```json +Input: + +{ + "a": "cool" +} + +Mappings: + +{ + "foo-exists": { + "@if": { + "exists": { "@path": "$.foo" }, + "then": true + } + } +} +=> +{} + +{ + "a": { + "@if": { + "exists": { "@path": "$.oops" }, + "then": { "@path": "$.a" } + } + } +} +=> +{} +``` + +### @path + +The @path directive resolves to the value at the given path. @path supports basic dot notation. Like JSONPath, you can include or omit the leading `$.` + +```json +Input: + +{ + "foo": { + "bar": 42, + "baz": [{ "num": 1 }, { "num": 2 }] + }, + "hello": "world" +} + +Mappings: + +{ "@path": "$.hello" } => "world" + +{ "@path": "$.foo.bar" } => 42 + +{ "@path": "$.foo.baz[0].num" } => 1 +``` + +### @template + +The @template directive resolves to a string replacing curly brace `{{}}` placeholders. + +```json +Input: + +{ + "traits": { + "name": "Mr. Rogers" + }, + "userId": "abc123" +} + +Mappings: + +{ "@template": "Hello, {{traits.name}}!" } => "Hello, Mr. Rogers!" + +{ "@template": "Hello, {{traits.fullName}}!" } => "Hello, !" + +{ "@template": "{{traits.name}} ({{userId}})" } => "Mr.Rogers (abc123)" +``` diff --git a/packages/core/src/mapping-kit/__tests__/index.test.ts b/packages/core/src/mapping-kit/__tests__/index.test.ts new file mode 100644 index 0000000000..ef1074a0a4 --- /dev/null +++ b/packages/core/src/mapping-kit/__tests__/index.test.ts @@ -0,0 +1,181 @@ +import { transform } from '../index' + +describe('validations', () => { + test('valid', () => { + expect(() => { + // @ts-expect-error unsupported, but doesn't throw + transform(['cool']) + }).not.toThrow() + expect(() => { + // @ts-expect-error unsupported, but doesn't throw + transform(123) + }).not.toThrow() + expect(() => { + transform({ foo: 'bar' }) + }).not.toThrow() + expect(() => { + // @ts-expect-error unsupported, but doesn't throw + transform('neat') + }).not.toThrow() + expect(() => { + transform({ '@path': '$.foo.bar' }) + }).not.toThrow() + expect(() => { + transform({ a: 1, b: { '@path': '$.foo.bar' } }) + }).not.toThrow() + }) + + test('invalid', () => { + expect(() => { + transform({ a: 1, '@field': '$.foo.bar' }) + }).toThrow() + expect(() => { + transform({ oops: { '@merge': [{}, 123] } }) + }).toThrow() + // Further validation tests are in validate.test.js + }) +}) + +describe('payload validations', () => { + test('invalid type', () => { + expect(() => { + // @ts-expect-error + transform({ a: 1 }, 123) + }).toThrowError() + expect(() => { + // @ts-expect-error + transform({ a: 1 }, []) + }).toThrowError() + }) +}) + +describe('no-op', () => { + test('empty mapping', () => { + const output = transform({}, { cool: false }) + expect(output).toStrictEqual({}) + }) + + test('pass-through mapping', () => { + const output = transform({ cool: true }, {}) + expect(output).toStrictEqual({ cool: true }) + }) +}) + +describe('@if', () => { + const payload = { a: 1, b: true, c: false, d: null } + + test('exists', () => { + let output = transform( + { + '@if': { + exists: { '@path': '$.a' }, + then: 1, + else: 2 + } + }, + payload + ) + expect(output).toStrictEqual(1) + + output = transform( + { + '@if': { + exists: { '@path': '$.d' }, + then: 1, + else: 2 + } + }, + payload + ) + expect(output).toStrictEqual(2) + + output = transform( + { + '@if': { + exists: { '@path': '$.x' }, + then: 1, + else: 2 + } + }, + payload + ) + expect(output).toStrictEqual(2) + }) +}) + +describe('@path', () => { + test('simple', () => { + const output = transform({ neat: { '@path': '$.foo' } }, { foo: 'bar' }) + expect(output).toStrictEqual({ neat: 'bar' }) + }) + + test('nested path', () => { + const output = transform({ neat: { '@path': '$.foo.bar' } }, { foo: { bar: 'baz' } }) + expect(output).toStrictEqual({ neat: 'baz' }) + }) + + test('nested directive', () => { + const output = transform({ '@path': { '@path': '$.foo' } }, { foo: 'bar', bar: 'baz' }) + expect(output).toStrictEqual('baz') + }) + + test('invalid path', () => { + const output = transform({ neat: { '@path': '$.oops' } }, { foo: 'bar' }) + expect(output).toStrictEqual({}) + }) + + test('invalid key type', () => { + expect(() => { + transform({ neat: { '@path': {} } }, { foo: 'bar' }) + }).toThrowError() + }) + + test('invalid nested value type', () => { + const output = transform({ neat: { '@path': '$.foo.bar.baz' } }, { foo: 'bar' }) + expect(output).toStrictEqual({}) + }) +}) + +describe('@template', () => { + test('basic', () => { + const output = transform({ '@template': 'Hello, {{who}}!' }, { who: 'World' }) + expect(output).toStrictEqual('Hello, World!') + }) + + test('nested fields', () => { + const output = transform({ '@template': 'Hello, {{who.name}}!' }, { who: { name: 'World' } }) + expect(output).toStrictEqual('Hello, World!') + }) + + test('no escaping', () => { + const output = transform({ '@template': '{{a}} {{{a}}}' }, { a: 'Hi' }) + expect(output).toStrictEqual('<b>Hi</b> Hi') + }) + + test('missing fields', () => { + const output = transform({ '@template': '{{oops.yo}}' }, {}) + expect(output).toStrictEqual('') + }) +}) + +describe('remove undefined values in objects', () => { + test('simple', () => { + expect(transform({ x: undefined }, {})).toEqual({}) + expect(transform({ x: null }, {})).toEqual({ x: null }) + expect(transform({ x: 'hi' }, {})).toEqual({ x: 'hi' }) + expect(transform({ x: 1 }, {})).toEqual({ x: 1 }) + expect(transform({ x: {} }, {})).toEqual({ x: {} }) + expect(transform({ x: undefined, y: 1, z: 'hi' }, {})).toEqual({ y: 1, z: 'hi' }) + }) + + test('nested', () => { + expect(transform({ x: { y: undefined, z: 1 }, foo: 1 }, {})).toEqual({ + x: { z: 1 }, + foo: 1 + }) + expect(transform({ x: { y: { z: undefined } }, foo: 1 }, {})).toEqual({ + x: { y: {} }, + foo: 1 + }) + }) +}) diff --git a/packages/core/src/mapping-kit/__tests__/placeholders.test.ts b/packages/core/src/mapping-kit/__tests__/placeholders.test.ts new file mode 100644 index 0000000000..e71c4a72bd --- /dev/null +++ b/packages/core/src/mapping-kit/__tests__/placeholders.test.ts @@ -0,0 +1,70 @@ +import { render } from '../placeholders' + +const fixtures = [ + { + template: '{{foo}}', + data: { foo: true }, + output: 'true' + }, + { + template: '{{foo}}', + data: { foo: 1 }, + output: '1' + }, + { + template: '{{foo}}', + data: { foo: { bar: 'baz' } }, + output: '[object Object]' + }, + { + template: '{{foo}}', + data: { foo: null }, + output: '' + }, + { + template: '{{foo}}', + data: { foo: undefined }, + output: '' + }, + { + template: '{{foo}}}', + data: { foo: 'hello' }, + output: 'hello}' + } +] + +describe('placeholders', () => { + test('throws if not given a template string', () => { + // @ts-expect-error testing bad input + expect(() => render({})).toThrowError(/Invalid template/) + }) + + test('returns the template as-is if there are no curlies', () => { + const value = render('Hello world') + expect(value).toBe('Hello world') + }) + + test('replaces non-existent data with empty string', () => { + const value = render('Hello {{foo}}') + expect(value).toBe('Hello ') + }) + + test('replaces placeholders with existing data', () => { + const value = render('Hello {{foo}} {{bar}}', { foo: 'Mr.', bar: 'Bean' }) + expect(value).toBe('Hello Mr. Bean') + }) + + test('replaces placeholders with existing nested data', () => { + const value = render('Hello {{foo.bar}}', { foo: { bar: 'World' } }) + expect(value).toBe('Hello World') + }) + + describe('replacements', () => { + for (const fixture of fixtures) { + test(`${fixture.template} -> ${JSON.stringify(fixture.data)}`, () => { + const value = render(fixture.template, fixture.data) + expect(value).toBe(fixture.output) + }) + } + }) +}) diff --git a/packages/core/src/mapping-kit/__tests__/validate.test.ts b/packages/core/src/mapping-kit/__tests__/validate.test.ts new file mode 100644 index 0000000000..d35e8bc083 --- /dev/null +++ b/packages/core/src/mapping-kit/__tests__/validate.test.ts @@ -0,0 +1,55 @@ +import { readdirSync } from 'fs' +import { join } from 'path' +import validate from '../validate' + +function* fixtures(subdir: string) { + const path = join(__dirname, '..', 'schema-fixtures', subdir) + const files = readdirSync(path, { withFileTypes: true }) + + for (const f of files) { + if (!f.isFile() || !f.name.endsWith('.json')) { + continue + } + + // eslint-disable-next-line + const { mapping, expectError } = require(join(path, f.name)) + yield { + name: f.name, + mapping, + expectError + } + } +} + +describe('validation', () => { + describe('passes valid mappings', () => { + for (const fixture of fixtures('valid')) { + it(fixture.name, () => { + expect(() => { + validate(fixture.mapping) + }).not.toThrow() + }) + } + }) + + describe('fails invalid mappings', () => { + for (const fixture of fixtures('invalid')) { + it(fixture.name, () => { + expect(fixture.expectError).toBeDefined() + expect(typeof fixture.expectError === 'string' || Array.isArray(fixture.expectError)).toBe(true) + + try { + validate(fixture.mapping) + } catch (error) { + if (typeof fixture.expectError === 'string') { + expect(error.message).toMatch(fixture.expectError) + } else { + for (const err of error) { + expect(fixture.expectError).toContain(err.message) + } + } + } + }) + } + }) +}) diff --git a/packages/core/src/mapping-kit/index.ts b/packages/core/src/mapping-kit/index.ts new file mode 100644 index 0000000000..6ba0946ae9 --- /dev/null +++ b/packages/core/src/mapping-kit/index.ts @@ -0,0 +1,132 @@ +import { get } from '../get' +import { JSONObject, JSONValue, JSONLike, JSONLikeObject } from '../json-object' +import { isDirective } from './is-directive' +import { render } from './placeholders' +import { realTypeOf, isObject, isArray } from '../real-type-of' +import { removeUndefined } from '../remove-undefined' +import validate from './validate' + +type Directive = (options: JSONValue, payload: JSONObject) => JSONLike +type StringDirective = (value: string, payload: JSONObject) => JSONLike + +interface Directives { + [directive: string]: Directive | undefined +} + +const directives: Directives = {} +const directiveRegExp = /^@[a-z][a-zA-Z0-9]+$/ + +function registerDirective(name: string, fn: Directive): void { + if (!directiveRegExp.exec(name)) { + throw new Error(`"${name}" is an invalid directive name`) + } + + directives[name] = fn +} + +function registerStringDirective(name: string, fn: StringDirective): void { + registerDirective(name, (value, payload) => { + const str = resolve(value, payload) + if (typeof str !== 'string') { + throw new Error(`${name}: expected string, got ${realTypeOf(str)}`) + } + + return fn(str, payload) + }) +} + +function runDirective(obj: JSONObject, payload: JSONObject): JSONLike { + const name = Object.keys(obj).find((key) => key.startsWith('@')) as string + const directiveFn = directives[name] + const value = obj[name] + + if (typeof directiveFn !== 'function') { + throw new Error(`${name} is not a valid directive, got ${realTypeOf(directiveFn)}`) + } + + return directiveFn(value, payload) +} + +registerDirective('@if', (opts, payload) => { + let condition = false + + if (!isObject(opts)) { + throw new Error('@if requires an object with an "exists" key') + } + + if (opts.exists !== undefined) { + const value = resolve(opts.exists, payload) + condition = value !== undefined && value !== null + } else { + throw new Error('@if requires an "exists" key') + } + + if (condition && opts.then !== undefined) { + return resolve(opts.then, payload) + } else if (!condition && opts.else) { + return resolve(opts.else, payload) + } +}) + +registerStringDirective('@path', (path, payload) => { + return get(payload, path.replace('$.', '')) +}) + +registerStringDirective('@template', (template: string, payload) => { + return render(template, payload) +}) + +/** + * Resolves a mapping value/object by applying the input payload based on directives + * *WARNING* This function mutates `mapping` when an object + * @param mapping - the mapping directives or raw values to resolve + * @param payload - the input data to apply to the mapping directives + * @todo support arrays or array directives? + */ +function resolve(mapping: JSONLike, payload: JSONObject): JSONLike { + if (!isObject(mapping) && !isArray(mapping)) { + return mapping + } + + if (isDirective(mapping)) { + return runDirective(mapping, payload) + } + + if (Array.isArray(mapping)) { + return mapping.map((value) => resolve(value, payload)) + } + + for (const key of Object.keys(mapping)) { + const value = mapping[key] + mapping[key] = resolve(value, payload) + } + + return mapping +} + +/** + * Validates and transforms a mapping by applying the input payload + * based on the directives and raw values defined in the mapping object + * @param mapping - the directives and raw values + * @param payload - the input data to apply to directives + */ +export function transform(mapping: JSONLikeObject, payload: JSONObject = {}): JSONObject { + const payloadType = realTypeOf(payload) + if (payloadType !== 'object') { + throw new Error(`payload must be an object, got ${payloadType}`) + } + + // throws if the mapping config is invalid + validate(mapping) + + const cloned = cloneJson(mapping) + const resolved = resolve(cloned, payload) + const cleaned = removeUndefined(resolved) + + // Cast because we know there are no `undefined` values anymore + return cleaned as JSONObject +} + +function cloneJson(obj: T): T { + return JSON.parse(JSON.stringify(obj)) +} diff --git a/packages/core/src/mapping-kit/is-directive.ts b/packages/core/src/mapping-kit/is-directive.ts new file mode 100644 index 0000000000..aee370cfce --- /dev/null +++ b/packages/core/src/mapping-kit/is-directive.ts @@ -0,0 +1,22 @@ +import { JSONObject } from '../json-object' +import { isObject } from '../real-type-of' + +export function isDirective(obj: unknown): obj is JSONObject { + if (!isObject(obj)) { + return false + } + + const keys = Object.keys(obj) + const hasDirectivePrefix = keys.some((key) => key.startsWith('@')) + if (!hasDirectivePrefix) { + return false + } + + // Ensure there aren't any other keys besides `@directive` or `_metadata` + const otherKeys = keys.filter((key) => !key.startsWith('@') && key !== '_metadata') + if (otherKeys.length === 0) { + return true + } + + return false +} diff --git a/packages/core/src/mapping-kit/placeholders.ts b/packages/core/src/mapping-kit/placeholders.ts new file mode 100644 index 0000000000..60936650bb --- /dev/null +++ b/packages/core/src/mapping-kit/placeholders.ts @@ -0,0 +1,55 @@ +import { get } from '../get' +import { realTypeOf } from '../real-type-of' + +const entityMap: Record = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', + '=': '=' +} + +function escapeHtml(value: unknown): string | unknown { + if (typeof value !== 'string') return value + + return value.replace(/[&<>"'`=/]/g, (match) => { + return entityMap[match] + }) +} + +/** + * Replaces curly brace placeholders in a template with real content + */ +export function render(template: string, data: unknown = {}): string { + if (typeof template !== 'string') { + throw new TypeError(`Invalid template! Template should be a "string" but ${realTypeOf(template)} was given.`) + } + + function replacer(chars: number, escape: boolean) { + return (match: string): string => { + // Remove the wrapping curly braces + match = match.slice(chars, -chars).trim() + + // Grab the value from data, if it exists + const value = get(data, match) + + // Replace with the value (or empty string) + if (escape) { + return String(escapeHtml(value) ?? '') + } + + return (value ?? '') as string + } + } + + return ( + template + // Replace unescaped content + .replace(/\{\{\{([^}]+)\}\}\}/g, replacer(3, false)) + // Replace escaped content + .replace(/\{\{([^}]+)\}\}/g, replacer(2, true)) + ) +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/@if-invalid-nested-field.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/@if-invalid-nested-field.json new file mode 100644 index 0000000000..eafcc482bc --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/@if-invalid-nested-field.json @@ -0,0 +1,11 @@ +{ + "mapping": { + "@if": { + "exists": true, + "then": { + "@oops": {} + } + } + }, + "expectError": "/@if/then has an invalid directive: @oops." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/@path-wrong-type.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/@path-wrong-type.json new file mode 100644 index 0000000000..0bde83f32b --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/@path-wrong-type.json @@ -0,0 +1,6 @@ +{ + "mapping": { + "@path": false + }, + "expectError": "/@path should be a string or a mapping directive but it is a boolean." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/@template-wrong-type.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/@template-wrong-type.json new file mode 100644 index 0000000000..00e3572021 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/@template-wrong-type.json @@ -0,0 +1,6 @@ +{ + "mapping": { + "@template": 123 + }, + "expectError": "/@template should be a string or a mapping directive but it is a number." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/mixed-directive-and-raw.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/mixed-directive-and-raw.json new file mode 100644 index 0000000000..aa79879657 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/mixed-directive-and-raw.json @@ -0,0 +1,7 @@ +{ + "mapping": { + "@path": "$.foo", + "bar": "baz" + }, + "expectError": "/ should only have one @-prefixed key but it has 2 keys." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-directives.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-directives.json new file mode 100644 index 0000000000..4b5364dfc0 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-directives.json @@ -0,0 +1,7 @@ +{ + "mapping": { + "@path": "$.foo", + "@bar": "baz" + }, + "expectError": "/ should only have one @-prefixed key but it has 2 keys." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-errors.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-errors.json new file mode 100644 index 0000000000..702e68eec1 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/multiple-errors.json @@ -0,0 +1,15 @@ +{ + "mapping": { + "obj": { + "@path": "foo.bar", + "oops": true + }, + "hash": { + "@template": {} + } + }, + "expectError": [ + "/obj should only have one @-prefixed key but it has 2 keys.", + "/hash/@template should be a string or a mapping directive but it is an object." + ] +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/invalid/unknown-directive.json b/packages/core/src/mapping-kit/schema-fixtures/invalid/unknown-directive.json new file mode 100644 index 0000000000..fcf2581031 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/invalid/unknown-directive.json @@ -0,0 +1,6 @@ +{ + "mapping": { + "@oops": {} + }, + "expectError": "/ has an invalid directive: @oops." +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@if-nested.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-nested.json new file mode 100644 index 0000000000..4772d3023c --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-nested.json @@ -0,0 +1,15 @@ +{ + "mapping": { + "@if": { + "exists": { + "@path": "$.foo" + }, + "then": { + "@path": "$.bar" + }, + "else": { + "@path": "$.baz" + } + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@if-partial.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-partial.json new file mode 100644 index 0000000000..3790d4afdc --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-partial.json @@ -0,0 +1,8 @@ +{ + "mapping": { + "@if": { + "exists": 1, + "else": 3 + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@if-simple.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-simple.json new file mode 100644 index 0000000000..8d6c8488ef --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@if-simple.json @@ -0,0 +1,9 @@ +{ + "mapping": { + "@if": { + "exists": 1, + "then": 2, + "else": 3 + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@path-nested.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@path-nested.json new file mode 100644 index 0000000000..af31043ea0 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@path-nested.json @@ -0,0 +1,7 @@ +{ + "mapping": { + "@path": { + "@path": "$.foo.bar" + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@path-simple.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@path-simple.json new file mode 100644 index 0000000000..d82904d2f9 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@path-simple.json @@ -0,0 +1,5 @@ +{ + "mapping": { + "@path": "$.foo.bar" + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@template-metadata.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-metadata.json new file mode 100644 index 0000000000..4a18e45d32 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-metadata.json @@ -0,0 +1,9 @@ +{ + "mapping": { + "@template": "{{foo.bar}}", + "_metadata": { + "type": "autocomplete", + "label": "X" + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@template-nested.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-nested.json new file mode 100644 index 0000000000..5b4d65b288 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-nested.json @@ -0,0 +1,7 @@ +{ + "mapping": { + "@template": { + "@path": "$.my.template" + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/@template-simple.json b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-simple.json new file mode 100644 index 0000000000..833372d052 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/@template-simple.json @@ -0,0 +1,5 @@ +{ + "mapping": { + "@template": "{{foo.bar}}" + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/raw-array.json b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-array.json new file mode 100644 index 0000000000..c8f10b62ce --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-array.json @@ -0,0 +1,3 @@ +{ + "mapping": [1, 2] +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object-nested.json b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object-nested.json new file mode 100644 index 0000000000..f504139551 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object-nested.json @@ -0,0 +1,7 @@ +{ + "mapping": { + "hello": { + "world!": true + } + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object.json b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object.json new file mode 100644 index 0000000000..e3160522f7 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-object.json @@ -0,0 +1,6 @@ +{ + "mapping": { + "cool": true, + "string": "right here!" + } +} diff --git a/packages/core/src/mapping-kit/schema-fixtures/valid/raw-string.json b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-string.json new file mode 100644 index 0000000000..dd725366f3 --- /dev/null +++ b/packages/core/src/mapping-kit/schema-fixtures/valid/raw-string.json @@ -0,0 +1,3 @@ +{ + "mapping": "foo" +} diff --git a/packages/core/src/mapping-kit/validate.ts b/packages/core/src/mapping-kit/validate.ts new file mode 100644 index 0000000000..21fcb36d91 --- /dev/null +++ b/packages/core/src/mapping-kit/validate.ts @@ -0,0 +1,246 @@ +import AggregateError from 'aggregate-error' +import { CustomError } from 'ts-custom-error' +import { isDirective } from './is-directive' +import { isObject, realTypeOf, Dictionary } from '../real-type-of' + +class ValidationError extends CustomError { + constructor(message: string, stack: string[] = []) { + super(`/${stack.join('/')} ${message}.`) + } +} + +function flatAggregate(errors: Error[]): Error[] { + const result: Error[] = [] + + errors.forEach((error) => { + if (error instanceof AggregateError) { + result.push(...error) + } else { + result.push(error) + } + }) + + return result +} + +function realTypeOrDirective(value: unknown) { + const type = realTypeOf(value) + if (type === 'object' && Object.keys(value as object).some((k) => k.startsWith('@'))) { + return 'directive' + } + return type +} + +type DirectiveValidator = (v: unknown, stack?: string[]) => void + +interface DirectiveValidators { + [directive: string]: DirectiveValidator | undefined +} + +const directives: DirectiveValidators = {} + +function validateDirective(obj: unknown, stack: string[] = []): void { + // "allow" non-directive objects so that we can throw a more descriptive error below + if (!isDirective(obj) && !isObject(obj)) { + const type = realTypeOf(obj) + throw new ValidationError(`should be a directive object but it is ${indefiniteArticle(type)} ${type}`, stack) + } + + const keys = Object.keys(obj) + const directiveKeys = keys.filter((key) => key.startsWith('@')) + + if (directiveKeys.length > 1) { + throw new ValidationError(`should only have one @-prefixed key but it has ${directiveKeys.length} keys`, stack) + } + + // Check that there aren't other keys besides @directive or _metadata + const otherKeys = keys.filter((key) => !key.startsWith('@') && key !== '_metadata') + + if (otherKeys.length > 0) { + throw new ValidationError(`should only have one @-prefixed key but it has ${keys.length} keys`, stack) + } + + const directiveKey = directiveKeys[0] + const fn = directives[directiveKey] + + if (typeof fn !== 'function') { + throw new ValidationError(`has an invalid directive: ${directiveKey}`, stack) + } + + fn(obj[directiveKey], stack) +} + +function validateDirectiveOrRaw(v: unknown, stack: string[] = []) { + const type = realTypeOrDirective(v) + switch (type) { + case 'directive': + return validateDirective(v, stack) + case 'object': + case 'array': + case 'boolean': + case 'string': + case 'number': + case 'null': + return + default: + throw new ValidationError( + `should be a mapping directive or a JSON value but it is ${indefiniteArticle(type)} ${type}`, + stack + ) + } +} + +function validateDirectiveOrString(v: unknown, stack: string[] = []) { + const type = realTypeOrDirective(v) + switch (type) { + case 'directive': + return validateDirective(v, stack) + case 'string': + return + default: + throw new ValidationError( + `should be a string or a mapping directive but it is ${indefiniteArticle(type)} ${type}`, + stack + ) + } +} + +function validateObject(value: unknown, stack: string[] = []) { + const type = realTypeOrDirective(value) + if (type !== 'object') { + throw new ValidationError(`should be an object but it is ${indefiniteArticle(type)} ${type}`, stack) + } + + const obj = value as Dictionary + const keys = Object.keys(obj) + + const directiveKey = keys.find((k) => k.charAt(0) === '@') + if (directiveKey) { + throw new ValidationError( + `shouldn't have directive (@-prefixed) keys but it has ${JSON.stringify(directiveKey)}`, + stack + ) + } + + const errors: Error[] = [] + keys.forEach((k) => { + try { + validate(obj[k], [...stack, k]) + } catch (e) { + errors.push(e) + } + }) + + if (errors.length) { + throw new AggregateError(flatAggregate(errors)) + } +} + +interface ValidateFields { + [key: string]: { + required?: DirectiveValidator + optional?: DirectiveValidator + } +} + +function validateObjectWithFields(input: unknown, fields: ValidateFields, stack: string[] = []) { + validateObject(input, stack) + + const errors: Error[] = [] + const obj = input as Dictionary + + Object.entries(fields).forEach(([prop, { required, optional }]) => { + try { + if (required) { + if (obj[prop] === undefined) { + throw new ValidationError(`should have field ${JSON.stringify(prop)} but it doesn't`, stack) + } + required(obj[prop], [...stack, prop]) + } else if (optional) { + if (obj[prop] !== undefined) { + optional(obj[prop], [...stack, prop]) + } + } + } catch (error) { + errors.push(error) + } + }) + + if (errors.length) { + throw new AggregateError(flatAggregate(errors)) + } +} + +function validateArray(arr: unknown, stack: string[] = []): void { + const type = realTypeOf(arr) + + if (type !== 'array') { + throw new ValidationError(`should be an array but it is ${indefiniteArticle(type)} ${type}`, stack) + } +} + +function directive(names: string[] | string, fn: DirectiveValidator): void { + if (!Array.isArray(names)) { + names = [names] + } + names.forEach((name) => { + directives[name] = (v: unknown, stack: string[] = []) => { + try { + fn(v, [...stack, name]) + } catch (e) { + if (e instanceof ValidationError || e instanceof AggregateError) { + throw e + } + + throw new ValidationError(e.message, stack) + } + } + }) +} + +directive('@if', (v, stack) => { + validateObjectWithFields( + v, + { + exists: { optional: validateDirectiveOrRaw }, + then: { optional: validateDirectiveOrRaw }, + else: { optional: validateDirectiveOrRaw } + }, + stack + ) +}) + +directive('@path', (v, stack) => { + validateDirectiveOrString(v, stack) +}) + +directive('@template', (v, stack) => { + validateDirectiveOrString(v, stack) +}) + +function indefiniteArticle(s: string): string { + switch (s.charAt(0)) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return 'an' + default: + return 'a' + } +} + +export default function validate(mapping: unknown, stack: string[] = []) { + switch (realTypeOrDirective(mapping)) { + case 'directive': + return validateDirective(mapping, stack) + case 'object': + return validateObject(mapping, stack) + case 'array': + return validateArray(mapping, stack) + default: + // All other types are valid "raw" mappings + return null + } +} diff --git a/packages/core/src/middleware/after-response/prepare-headers.ts b/packages/core/src/middleware/after-response/prepare-headers.ts new file mode 100644 index 0000000000..8664e0589e --- /dev/null +++ b/packages/core/src/middleware/after-response/prepare-headers.ts @@ -0,0 +1,25 @@ +import type { AfterResponseHook } from '../../request-client' + +const headersToObject = (headers: Headers) => { + const obj: Record = {} + + // @ts-ignore the types are wrong for Headers + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + for (const [key, value] of headers.entries()) { + obj[key] = value + } + + return obj +} + +const prepareHeaders: AfterResponseHook = async (_request, _options, response) => { + // Adds `toJSON()` support for headers to get a plain object + Object.defineProperty(response.headers, 'toJSON', { + enumerable: false, + value: () => headersToObject(response.headers) + }) + + return response +} + +export default prepareHeaders diff --git a/packages/core/src/middleware/after-response/prepare-response.ts b/packages/core/src/middleware/after-response/prepare-response.ts new file mode 100644 index 0000000000..6186460220 --- /dev/null +++ b/packages/core/src/middleware/after-response/prepare-response.ts @@ -0,0 +1,30 @@ +import type { AfterResponseHook } from '../../request-client' +import type { ModifiedResponse } from '../../types' + +const prepareResponse: AfterResponseHook = async (_request, _options, response) => { + const modifiedResponse = response as ModifiedResponse + + // Clone the response before reading the body to avoid + // `TypeError: body used already` elsewhere + const clone = response.clone() + const content = await clone.text() + let data: unknown + + try { + if (modifiedResponse.headers.get('content-type')?.includes('application/json')) { + data = JSON.parse(content) + } else { + // TODO handle form urlencoded responses? + data = content + } + } catch (_error) { + // do nothing + } + + modifiedResponse.content = content + modifiedResponse.data = data + + return modifiedResponse +} + +export default prepareResponse diff --git a/packages/core/src/middleware/before-request/add-basic-auth-header.ts b/packages/core/src/middleware/before-request/add-basic-auth-header.ts new file mode 100644 index 0000000000..4358807aaa --- /dev/null +++ b/packages/core/src/middleware/before-request/add-basic-auth-header.ts @@ -0,0 +1,19 @@ +import btoa from 'btoa-lite' +import type { BeforeRequestHook } from '../../request-client' + +const addBasicAuthHeader: BeforeRequestHook = (options) => { + if (options.username || options.password) { + const username = options.username || '' + const password = options.password || '' + const encoded = btoa(`${username}:${password}`) + const authorization = `Basic ${encoded}` + + return { + headers: { + Authorization: authorization + } + } + } +} + +export default addBasicAuthHeader diff --git a/packages/core/src/omit.ts b/packages/core/src/omit.ts new file mode 100644 index 0000000000..681939de50 --- /dev/null +++ b/packages/core/src/omit.ts @@ -0,0 +1,9 @@ +/** + * Lightweight alternative to lodash.omit. Faster, and way less code. + */ +export function omit(obj: T, keys: K) { + return Object.keys(obj).reduce((newObject, key) => { + if (keys.indexOf(key) === -1) newObject[key] = (obj as Record)[key] + return newObject + }, {} as Record) as Omit +} diff --git a/packages/core/src/real-type-of.ts b/packages/core/src/real-type-of.ts new file mode 100644 index 0000000000..5b28e2e7fb --- /dev/null +++ b/packages/core/src/real-type-of.ts @@ -0,0 +1,35 @@ +/** + * Gets the "true" type of an object, since JS `typeof` often returns `'object'` + * for arrays, date, regexp, and null + * @param obj - the object to get the real type of + */ +export function realTypeOf(obj: unknown) { + return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() as + | 'string' + | 'number' + | 'bigint' + | 'boolean' + | 'symbol' + | 'undefined' + | 'object' + | 'function' + | 'null' + | 'array' + | 'regexp' +} + +export interface Dictionary { + [key: string]: T +} + +export function isObject(value: unknown): value is Dictionary { + return realTypeOf(value) === 'object' +} + +export function isArray(value: unknown): value is unknown[] { + return Array.isArray(value) +} + +export function isString(value: unknown): value is string { + return typeof value === 'string' +} diff --git a/packages/core/src/remove-undefined.ts b/packages/core/src/remove-undefined.ts new file mode 100644 index 0000000000..6f823ecdb3 --- /dev/null +++ b/packages/core/src/remove-undefined.ts @@ -0,0 +1,23 @@ +import { isObject } from './real-type-of' + +type Dictionary = { + [key: string]: T +} + +export function removeUndefined(value: T): T { + if (Array.isArray(value)) { + return value.map((item) => removeUndefined(item)) as unknown as T + } else if (isObject(value)) { + const cleaned: Dictionary = Object.assign({}, value) + Object.keys(cleaned).forEach((key) => { + if (cleaned[key] === undefined) { + delete cleaned[key] + } else { + cleaned[key] = removeUndefined(cleaned[key]) + } + }) + return cleaned as unknown as T + } + + return value +} diff --git a/packages/core/src/request-client.ts b/packages/core/src/request-client.ts new file mode 100644 index 0000000000..68a80e543a --- /dev/null +++ b/packages/core/src/request-client.ts @@ -0,0 +1,312 @@ +import AbortController from 'abort-controller' +import { CustomError } from 'ts-custom-error' +import { URL, URLSearchParams } from 'url' +import fetch, { Headers, Request, Response } from './fetch' +import { isObject } from './real-type-of' + +/** + * The supported request options you can use with the request client + */ +export interface RequestOptions extends Omit { + /** + * Simplified header format to reduce variation + */ + headers?: Record + /** + * Shortcut for sending JSON. Use instead of `body`. + * Accepts any plain object or value, which will be `JSON.stringify()`'d and sent in the body with the correct header set. + */ + json?: unknown + /** + * HTTP method used to make the request. + * Internally, the standard methods (`GET`, `POST`, `PUT`, `PATCH`, `HEAD` and `DELETE`) are uppercased in order to avoid server errors due to case sensitivity. + * @default 'get' + */ + method?: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' + /** + * When provided, will automatically apply basic authentication + */ + password?: string + /** + * Search parameters to include int he request URL. Setting this will override existing search parameters in the input URL. + * Accepts [`URLSearchParams()`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams) + */ + searchParams?: URLSearchParams | Record + /** + * Throw an error when, after redirects, the response has a non-2xx status code. + * @default true + */ + throwHttpErrors?: boolean + /** + * Timeout in milliseconds for getting a response. + * If set to `false` there will be no timeout. + * @default 10000 + */ + timeout?: number | false + /** + * When provided, will automatically apply basic authentication + */ + username?: string +} + +/** + * All request options including before/after hooks + */ +export interface AllRequestOptions extends RequestOptions { + /** + * Hooks that execute before a request is sent. + * Useful to modify the options that a request will use + */ + beforeRequest?: BeforeRequestHook[] + /** + * Hooks that executes after a response is received. + * Useful for logging, cleanup, or modifying the response object + */ + afterResponse?: AfterResponseHook[] +} + +export interface NormalizedOptions extends Omit { + // the merging process turns these into a Headers object, but you can pass in more + headers: RequestInit['headers'] + // method is expected to be defined at this point + method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' +} + +type MaybePromise = Promise | T + +/** A hook that executes before a request is executed. Useful to modify the options that a request will use */ +export type BeforeRequestHook = (options: NormalizedOptions) => MaybePromise + +/** A hook that executes after a response is received. Useful for logging, cleanup, or modifying the response object */ +export type AfterResponseHook = ( + request: Request, + options: NormalizedOptions, + response: Response +) => MaybePromise + +// We need the loose definition of "object" to iterate over things like `[object Headers]` +const isObjectLike = (value: unknown): value is { [key: string]: unknown } => { + return value !== null && typeof value === 'object' +} + +/** Merges two sets of headers intelligently */ +function mergeHeaders(source1: RequestInit['headers'], source2: RequestInit['headers']) { + const result = new Headers(source1 || {}) + const source = new Headers(source2 || {}) + + source.forEach((value, key) => { + // `value` may be the string `undefined` when the source is a Headers object + if (value === 'undefined') { + result.delete(key) + } else { + result.set(key, value) + } + }) + + return result +} + +/** Deeply merge multiple objects or arrays. Arrays get concatenated. */ +function merge(...sources: T[]): T { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let result: any = {} + let headers: RequestInit['headers'] = {} + + for (const source of sources) { + if (Array.isArray(source)) { + if (!Array.isArray(result)) { + result = [] + } + + // Concatenate arrays (e.g. hooks) + result = [...result, ...source] + } else if (isObjectLike(source)) { + // eslint-disable-next-line prefer-const + for (let [key, value] of Object.entries(source)) { + // Recurse over objects + if (isObjectLike(value) && key in result) { + value = merge(result[key], value) + } + + // Update the key's value in our copy + result[key] = value + } + + // Merge headers more carefully to handle duplicates + if (isObjectLike(source.headers)) { + headers = mergeHeaders(headers, source.headers as RequestInit['headers']) + } + } + + // Assign after we've merged all the headers because they are handled specially + result.headers = headers + } + + return result as T +} + +/** + * Validates and merges request options + */ +function mergeOptions(...sources: Array): AllRequestOptions { + for (const source of sources) { + if (!isObject(source)) { + throw new TypeError(`The 'options' argument must be an object`) + } + } + + return merge({}, ...sources) as AllRequestOptions +} + +function getRequestMethod(method: T): T { + return method.toUpperCase() as T +} + +/** Error thrown when a response has a non-2xx response. */ +export class HTTPError extends CustomError { + request: Request + response: Response + options: NormalizedOptions + + constructor(response: Response, request: Request, options: NormalizedOptions) { + super(response.statusText ?? String(response.status ?? 'Unknown response error')) + this.response = response + this.request = request + this.options = options + } +} + +/** Error thrown when a request is aborted because of a client timeout. */ +export class TimeoutError extends CustomError { + request: Request + options: NormalizedOptions + + constructor(request: Request, options: NormalizedOptions) { + super(`Request timed out`) + this.request = request + this.options = options + } +} + +/** + * Given a request, reject the request when a timeout is exceeded + */ +function timeoutFetch( + request: Request, + abortController: AbortController, + options: NormalizedOptions +): Promise { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + if (abortController) { + abortController.abort() + } + + reject(new TimeoutError(request, options)) + }, options.timeout as number) + + void fetch(request) + .then(resolve) + .catch(reject) + .then(() => clearTimeout(timer)) + }) +} + +class RequestClient { + private abortController: AbortController + private request: Request + private options: NormalizedOptions + + constructor(url: string, options: AllRequestOptions = {}) { + this.setOptions(url, options) + } + + private setOptions(url: string, options: AllRequestOptions) { + this.options = { + ...options, + method: getRequestMethod(options.method ?? 'get'), + throwHttpErrors: options.throwHttpErrors !== false, + timeout: options.timeout ?? 10000 + } as NormalizedOptions + + // Timeout support. Use our own abort controller so consumers can pass in their own `signal` + // if they wish to use timeouts alongside other logic to abort a request + this.abortController = new AbortController() + if (this.options.signal) { + // Listen to consumer abort events to also abort our internal controller + this.options.signal.addEventListener('abort', () => { + this.abortController.abort() + }) + } + + // Use our internal abort controller for fetch + this.options.signal = this.abortController.signal + + // Construct a request object to send to the Fetch API + this.request = new Request(url, this.options) + + // Parse search params and merge them with the request URL + if (this.options.searchParams) { + // The type is a bit too restrictive, since you can pass in other primitives like `{ foo: 1, bar: true }` + const searchParams = new URLSearchParams(this.options.searchParams as URLSearchParams | Record) + const url = new URL(this.request.url) + url.search = searchParams.toString() + + // Update the request object with the new url including the search params + this.request = new Request(new Request(url.toString(), this.request), this.options) + } + + // Automatically handle json header + stringification as a convenience when using `json` option + if (this.options.json !== undefined) { + this.options.body = JSON.stringify(this.options.json) + this.request.headers.set('content-type', 'application/json') + this.request = new Request(this.request, { body: this.options.body }) + } + } + + async executeRequest(): Promise { + let response = await this.fetch() + + for (const hook of this.options.afterResponse ?? []) { + const modifiedResponse = await hook(this.request, this.options, response) + if (modifiedResponse instanceof Response) { + response = modifiedResponse + } + } + + if (!response.ok && this.options.throwHttpErrors) { + throw new HTTPError(response, this.request, this.options) + } + + return response as T + } + + /** + * Make a fetch request, running all beforeRequest hooks, and optionally wrap in a timeout + */ + private async fetch() { + for (const hook of this.options.beforeRequest ?? []) { + const newOptions = await hook({ ...this.options }) + if (newOptions && isObject(newOptions)) { + this.setOptions(this.request.url, mergeOptions(this.options, newOptions)) + } + } + + if (this.options.timeout === false) { + return fetch(this.request.clone()) + } + + return timeoutFetch(this.request.clone(), this.abortController, this.options) + } +} + +/** + * Creates a new instance of the fetch request client, + * optionally with default configuration to apply to all requests made with the client + */ +export default function createInstance(defaults: AllRequestOptions = {}) { + const client = (url: string, options: RequestOptions = {}) => + new RequestClient(url, mergeOptions(defaults, options)).executeRequest() + client.extend = (newDefaults: AllRequestOptions) => createInstance(mergeOptions(defaults, newDefaults)) + return client +} diff --git a/packages/core/src/segment-event.ts b/packages/core/src/segment-event.ts new file mode 100644 index 0000000000..ec244c1094 --- /dev/null +++ b/packages/core/src/segment-event.ts @@ -0,0 +1,158 @@ +import { JSONValue } from './json-object' + +type ID = string | null | undefined + +type CompactMetricType = 'g' | 'c' + +interface CompactMetric { + m: string // metric name + v: number // value + k: CompactMetricType + t: string[] // tags + e: number // timestamp in unit milliseconds +} + +export type Integrations = { + All?: boolean + [integration: string]: boolean | undefined +} + +export type Options = { + integrations?: Integrations + anonymousId?: ID + timestamp?: Date | string + context?: AnalyticsContext + traits?: object + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any +} + +interface AnalyticsContext { + page?: { + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L151} + */ + path?: string + referrer?: string + search?: string + title?: string + url?: string + } + metrics?: CompactMetric[] + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L285} + */ + userAgent?: string + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L286-L289} + */ + locale?: string + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L290-L291} + */ + library?: { + name: string + version: string + } + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L292-L301} + */ + traits?: { + crossDomainId: string + } + + /** + * utm params + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L303-L305} + * {@link https://github.com/segmentio/utm-params/blob/master/lib/index.js#L49} + */ + campaign?: { + /** + * This can also come from the "utm_campaign" param + * + * {@link https://github.com/segmentio/utm-params/blob/master/lib/index.js#L40} + */ + name: string + term: string + source: string + medium: string + content: string + } + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L415} + */ + referrer?: { + btid?: string + urid?: string + } + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L322} + */ + amp?: { + id: string + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any +} + +export interface SegmentEvent { + messageId?: string + + type: 'track' | 'page' | 'identify' | 'group' | 'alias' | 'screen' + + // page specific + category?: string + name?: string + + properties?: object & { + [k: string]: JSONValue + } + + // TODO: Narrow types (i.e. only show traits for `track` and `group`) + traits?: object & { + [k: string]: JSONValue + } + + integrations?: Integrations + context?: AnalyticsContext + options?: Options + + userId?: ID + anonymousId?: ID + groupId?: ID + previousId?: ID + + event?: string + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L284} + */ + writeKey?: string + + receivedAt?: Date | string + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L151} + */ + sentAt?: Date | string + + /** + * {@link https://github.com/segmentio/analytics.js-integrations/blob/2d5c637c022d2661c23449aed237d0d546bf062d/integrations/segmentio/lib/index.js#L311-L320} + */ + _metadata?: { + failedInitializations?: unknown[] + bundled?: string[] + unbundledIntegrations?: string[] + nodeVersion?: string + } + + timestamp?: Date | string +} diff --git a/packages/core/src/time.ts b/packages/core/src/time.ts new file mode 100644 index 0000000000..1581c23a82 --- /dev/null +++ b/packages/core/src/time.ts @@ -0,0 +1,13 @@ +/** + * Gets hrtime for logging/metrics. + */ +export function time(): bigint { + return process.hrtime.bigint() +} + +/** + * Calculates the duration in decimal milliseconds for logging/metrics purposes. + */ +export function duration(start: bigint, stop: bigint): number { + return Number(stop - start) / 1000000 +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000000..625b991495 --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,11 @@ +/** A modified response object that is handled by the `prepareResponse` hook */ +export interface ModifiedResponse extends Response { + /** The raw response content as a string – same as `await response.text()` */ + content: string + /** The parsed content string into a JavaScript object, if applicable */ + data: unknown extends T ? undefined | unknown : T + /** The headers object with a shortcut method to get headers as an object */ + headers: Headers & { + toJSON: () => Record + } +} diff --git a/packages/core/test/create-test-server.d.ts b/packages/core/test/create-test-server.d.ts new file mode 100644 index 0000000000..08f70dbe76 --- /dev/null +++ b/packages/core/test/create-test-server.d.ts @@ -0,0 +1,19 @@ +declare module 'create-test-server' { + import { Express } from 'express' + + function createTestServer(options?: unknown): Promise + + export = createTestServer + + namespace createTestServer { + export interface TestServer extends Express { + caCert: string | Buffer | Array + port: number + url: string + sslPort: number + sslUrl: string + + close: () => Promise + } + } +} diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json new file mode 100644 index 0000000000..79aa956d48 --- /dev/null +++ b/packages/core/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "exclude": ["**/__tests__/**/*.ts", "benchmarks"], + "include": ["src"] +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000000..9f56d24f6a --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "module": "esnext", + "removeComments": false, + "baseUrl": ".", + "paths": { + "@segment/ajv-human-errors": ["../ajv-human-errors"], + "@segment/destination-subscriptions": ["../destination-subscriptions/src"] + } + }, + "exclude": [], + "include": ["src", "test", "benchmarks"] +} diff --git a/packages/destination-actions/package.json b/packages/destination-actions/package.json new file mode 100644 index 0000000000..9f01952844 --- /dev/null +++ b/packages/destination-actions/package.json @@ -0,0 +1,52 @@ +{ + "name": "@segment/destination-actions", + "description": "Destination Actions engine and definitions.", + "version": "2.4.2", + "repository": { + "type": "git", + "url": "https://github.com/segmentio/action-destinations", + "directory": "packages/destination-actions" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "package.json" + ], + "engineStrict": true, + "license": "UNLICENSED", + "publishConfig": { + "access": "restricted", + "registry": "https://registry.npmjs.org" + }, + "scripts": { + "build": "yarn clean && yarn tsc -b tsconfig.build.json", + "clean": "tsc -b tsconfig.build.json --clean", + "postclean": "rm -rf dist", + "prepublishOnly": "yarn build", + "test": "jest", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/jest": "^26.0.23", + "jest": "^26.6.3", + "nock": "^13.0.11" + }, + "dependencies": { + "@segment/actions-core": "^2.2.1", + "dayjs": "^1.10.3" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "modulePathIgnorePatterns": [ + "/dist/" + ], + "moduleNameMapper": { + "@segment/actions-core": "/../core/src/index.ts" + }, + "setupFilesAfterEnv": [ + "/test/setup-after-env.ts" + ] + } +} diff --git a/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts b/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts new file mode 100644 index 0000000000..bd22a81135 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/__tests__/amplitude.test.ts @@ -0,0 +1,227 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import Amplitude from '../index' + +const testDestination = createTestIntegration(Amplitude) +const timestamp = new Date().toISOString() + +describe('Amplitude', () => { + describe('logEvent', () => { + it('should work with default mappings', async () => { + const event = createTestEvent({ timestamp, event: 'Test Event' }) + + nock('https://api2.amplitude.com/2').post('/httpapi').reply(200, {}) + + const responses = await testDestination.testAction('logEvent', { event, useDefaultMappings: true }) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].data).toMatchObject({}) + expect(responses[0].options.json).toMatchObject({ + api_key: undefined, + events: expect.arrayContaining([ + expect.objectContaining({ + event_type: 'Test Event', + city: 'San Francisco', + country: 'United States' + }) + ]) + }) + }) + + it('should accept null for user_id', async () => { + const event = createTestEvent({ timestamp, userId: null, event: 'Null User' }) + + nock('https://api2.amplitude.com/2').post('/httpapi').reply(200, {}) + + const responses = await testDestination.testAction('logEvent', { event, useDefaultMappings: true }) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].data).toMatchObject({}) + expect(responses[0].options.json).toMatchObject({ + api_key: undefined, + events: expect.arrayContaining([ + expect.objectContaining({ + event_type: 'Null User', + user_id: null + }) + ]) + }) + }) + + it('should work with default mappings', async () => { + const event = createTestEvent({ + event: 'Order Completed', + timestamp, + properties: { + revenue: 1_999, + products: [ + { + quantity: 1, + productId: 'Bowflex Treadmill 10', + price: 1_999 + } + ] + } + }) + + nock('https://api2.amplitude.com/2').post('/httpapi').reply(200, {}) + + const responses = await testDestination.testAction('logEvent', { event, useDefaultMappings: true }) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].options.json).toMatchObject({ + api_key: undefined, + events: expect.arrayContaining([ + expect.objectContaining({ + event_type: 'Order Completed', + revenue: 1_999, + event_properties: event.properties + }), + expect.objectContaining({ + event_type: 'Product Purchased', + // @ts-ignore i know what i'm doing + event_properties: event.properties.products[0] + }) + ]) + }) + }) + + it('should work with per product revenue tracking', async () => { + nock('https://api2.amplitude.com/2').post('/httpapi').reply(200, {}) + + const event = createTestEvent({ + event: 'Order Completed', + timestamp, + properties: { + revenue: 1_999, + products: [ + { + quantity: 1, + productId: 'Bowflex Treadmill 10', + revenue: 1_999 + } + ] + } + }) + + const mapping = { + trackRevenuePerProduct: true + } + + const responses = await testDestination.testAction('logEvent', { event, mapping, useDefaultMappings: true }) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].options.json).toMatchObject({ + api_key: undefined, + events: expect.arrayContaining([ + expect.objectContaining({ + event_type: 'Order Completed', + event_properties: event.properties + }), + expect.objectContaining({ + event_type: 'Product Purchased', + revenue: 1_999, + // @ts-ignore i know what i'm doing + event_properties: event.properties.products[0] + }) + ]) + }) + }) + }) + + describe('mapUser', () => { + it('should work with default mappings', async () => { + const event = createTestEvent({ + type: 'alias', + userId: 'some-user-id', + previousId: 'some-previous-user-id' + }) + + nock('https://api.amplitude.com').post('/usermap').reply(200, {}) + + const responses = await testDestination.testAction('mapUser', { event, useDefaultMappings: true }) + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + expect(responses[0].data).toMatchObject({}) + expect(responses[0].options.body).toMatchInlineSnapshot(` + URLSearchParams { + Symbol(query): Array [ + "api_key", + "undefined", + "mapping", + "[{\\"user_id\\":\\"some-previous-user-id\\",\\"global_user_id\\":\\"some-user-id\\"}]", + ], + Symbol(context): null, + } + `) + }) + }) + + describe('groupIdentifyUser', () => { + const event = createTestEvent({ + anonymousId: 'some-anonymous-id', + timestamp: '2021-04-12T16:32:37.710Z', + type: 'group', + userId: 'some-user-id', + traits: { + 'some-trait-key': 'some-trait-value' + } + }) + + const mapping = { + insert_id: 'some-insert-id', + group_type: 'some-type', + group_value: 'some-value' + } + + it('should fire identify call to Amplitude', async () => { + nock('https://api.amplitude.com').post('/identify').reply(200, {}) + nock('https://api.amplitude.com').post('/groupidentify').reply(200, {}) + + const [response] = await testDestination.testAction('groupIdentifyUser', { + event, + mapping, + useDefaultMappings: true + }) + + expect(response.status).toBe(200) + expect(response.data).toMatchObject({}) + expect(response.options.body).toMatchInlineSnapshot(` + URLSearchParams { + Symbol(query): Array [ + "api_key", + "undefined", + "identification", + "[{\\"device_id\\":\\"some-anonymous-id\\",\\"groups\\":{\\"some-type\\":\\"some-value\\"},\\"insert_id\\":\\"some-insert-id\\",\\"library\\":\\"segment\\",\\"time\\":1618245157710,\\"user_id\\":\\"some-user-id\\",\\"user_properties\\":{\\"some-type\\":\\"some-value\\"}}]", + ], + Symbol(context): null, + } + `) + }) + + it('should fire groupidentify call to Amplitude', async () => { + nock('https://api.amplitude.com').post('/identify').reply(200, {}) + nock('https://api.amplitude.com').post('/groupidentify').reply(200, {}) + + const [, response] = await testDestination.testAction('groupIdentifyUser', { + event, + mapping, + useDefaultMappings: true + }) + + expect(response.status).toBe(200) + expect(response.data).toMatchObject({}) + expect(response.options.body).toMatchInlineSnapshot(` + URLSearchParams { + Symbol(query): Array [ + "api_key", + "undefined", + "identification", + "[{\\"group_properties\\":{\\"some-trait-key\\":\\"some-trait-value\\"},\\"group_value\\":\\"some-value\\",\\"group_type\\":\\"some-type\\",\\"library\\":\\"segment\\"}]", + ], + Symbol(context): null, + } + `) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/amplitude/event-schema.ts b/packages/destination-actions/src/destinations/amplitude/event-schema.ts new file mode 100644 index 0000000000..b995b141cb --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/event-schema.ts @@ -0,0 +1,300 @@ +import { InputField } from '@segment/actions-core' + +/** + * The common fields defined by Amplitude's events api + * @see {@link https://developers.amplitude.com/docs/http-api-v2#keys-for-the-event-argument} + */ +export const eventSchema: Record = { + user_id: { + label: 'User ID', + type: 'string', + allowNull: true, + description: + 'A readable ID specified by you. Must have a minimum length of 5 characters. Required unless device ID is present. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event.', + default: { + '@path': '$.userId' + } + }, + device_id: { + label: 'Device ID', + type: 'string', + description: + 'A device-specific identifier, such as the Identifier for Vendor on iOS. Required unless user ID is present. If a device ID is not sent with the event, it will be set to a hashed version of the user ID.', + default: { + '@if': { + exists: { '@path': '$.context.device.id' }, + then: { '@path': '$.context.device.id' }, + else: { '@path': '$.anonymousId' } + } + } + }, + event_type: { + label: 'Event Type', + type: 'string', + description: 'A unique identifier for your event.', + required: true, + default: { + '@path': '$.event' + } + }, + session_id: { + label: 'Session ID', + type: 'datetime', + description: + 'The start time of the session, necessary if you want to associate events with a particular system. To use automatic Amplitude session tracking in browsers, enable Analytics 2.0 on your connected source.', + default: { + '@path': '$.integrations.Amplitude.session_id' + } + }, + time: { + label: 'Timestamp', + type: 'datetime', + description: + 'The timestamp of the event. If time is not sent with the event, it will be set to the request upload time.', + default: { + '@path': '$.timestamp' + } + }, + event_properties: { + label: 'Event Properties', + type: 'object', + description: + 'An object of key-value pairs that represent additional data to be sent along with the event. You can store property values in an array, but note that Amplitude only supports one-dimensional arrays. Date values are transformed into string values. Object depth may not exceed 40 layers.', + default: { + '@path': '$.properties' + } + }, + user_properties: { + label: 'User Properties', + type: 'object', + description: + 'An object of key-value pairs that represent additional data tied to the user. You can store property values in an array, but note that Amplitude only supports one-dimensional arrays. Date values are transformed into string values. Object depth may not exceed 40 layers.', + default: { + '@path': '$.traits' + } + }, + groups: { + label: 'Groups', + type: 'object', + description: + 'Groups of users for the event as an event-level group. You can only track up to 5 groups. **Note:** This Amplitude feature is only available to Enterprise customers who have purchased the Accounts add-on.' + }, + app_version: { + label: 'App Version', + type: 'string', + description: 'The current version of your application.', + default: { + '@path': '$.context.app.version' + } + }, + platform: { + label: 'Platform', + type: 'string', + description: 'Platform of the device.', + default: { + '@path': '$.context.device.type' + } + }, + os_name: { + label: 'OS Name', + type: 'string', + description: 'The name of the mobile operating system or browser that the user is using.', + default: { + '@path': '$.context.os.name' + } + }, + os_version: { + label: 'OS Version', + type: 'string', + description: 'The version of the mobile operating system or browser the user is using.', + default: { + '@path': '$.context.os.version' + } + }, + device_brand: { + label: 'Device Brand', + type: 'string', + description: 'The device brand that the user is using.', + default: { + '@path': '$.context.device.brand' + } + }, + device_manufacturer: { + label: 'Device Manufacturer', + type: 'string', + description: 'The device manufacturer that the user is using.', + default: { + '@path': '$.context.device.manufacturer' + } + }, + device_model: { + label: 'Device Model', + type: 'string', + description: 'The device model that the user is using.', + default: { + '@path': '$.context.device.model' + } + }, + carrier: { + label: 'Carrier', + type: 'string', + description: 'The carrier that the user is using.', + default: { + '@path': '$.context.network.carrier' + } + }, + country: { + label: 'Country', + type: 'string', + description: 'The current country of the user.', + default: { + '@path': '$.context.location.country' + } + }, + region: { + label: 'Region', + type: 'string', + description: 'The current region of the user.', + default: { + '@path': '$.context.location.region' + } + }, + city: { + label: 'City', + type: 'string', + description: 'The current city of the user.', + default: { + '@path': '$.context.location.city' + } + }, + dma: { + label: 'Designated Market Area', + type: 'string', + description: 'The current Designated Market Area of the user.' + }, + language: { + label: 'Language', + type: 'string', + description: 'The language set by the user.', + default: { + '@path': '$.context.locale' + } + }, + price: { + label: 'Price', + type: 'number', + description: + 'The price of the item purchased. Required for revenue data if the revenue field is not sent. You can use negative values to indicate refunds.', + default: { + '@path': '$.properties.price' + } + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'The quantity of the item purchased. Defaults to 1 if not specified.', + default: { + '@path': '$.properties.quantity' + } + }, + revenue: { + label: 'Revenue', + type: 'number', + description: + 'Revenue = price * quantity. If you send all 3 fields of price, quantity, and revenue, then (price * quantity) will be used as the revenue value. You can use negative values to indicate refunds. **Note:** You will need to explicitly set this if you are using the Amplitude in cloud-mode.', + default: { + '@path': '$.properties.revenue' + } + }, + productId: { + label: 'Product ID', + type: 'string', + description: 'An identifier for the item purchased. You must send a price and quantity or revenue with this field.', + default: { + '@path': '$.properties.productId' + } + }, + revenueType: { + label: 'Revenue Type', + type: 'string', + description: + 'The type of revenue for the item purchased. You must send a price and quantity or revenue with this field.', + default: { + '@path': '$.properties.revenueType' + } + }, + location_lat: { + label: 'Latitude', + type: 'number', + description: 'The current Latitude of the user.', + default: { + '@path': '$.context.location.latitude' + } + }, + location_lng: { + label: 'Longtitude', + type: 'number', + description: 'The current Longitude of the user.', + default: { + '@path': '$.context.location.longitude' + } + }, + ip: { + label: 'IP Address', + type: 'string', + description: + 'The IP address of the user. Use "$remote" to use the IP address on the upload request. Amplitude will use the IP address to reverse lookup a user\'s location (city, country, region, and DMA). Amplitude has the ability to drop the location and IP address from events once it reaches our servers. You can submit a request to Amplitude\'s platform specialist team here to configure this for you.', + default: { + '@path': '$.context.ip' + } + }, + idfa: { + label: 'Identifier For Advertiser (IDFA)', + type: 'string', + description: 'Identifier for Advertiser. _(iOS)_', + default: { + '@if': { + exists: { '@path': '$.context.device.advertisingId' }, + then: { '@path': '$.context.device.advertisingId' }, + else: { '@path': '$.context.device.idfa' } + } + } + }, + idfv: { + label: 'Identifier For Vendor (IDFV)', + type: 'string', + description: 'Identifier for Vendor. _(iOS)_', + default: { + '@path': '$.context.device.id' + } + }, + adid: { + label: 'Google Play Services Advertising ID', + type: 'string', + description: 'Google Play Services advertising ID. _(Android)_', + default: { + '@if': { + exists: { '@path': '$.context.device.advertisingId' }, + then: { '@path': '$.context.device.advertisingId' }, + else: { '@path': '$.context.device.idfa' } + } + } + }, + android_id: { + label: 'Android ID', + type: 'string', + description: 'Android ID (not the advertising ID). _(Android)_' + }, + event_id: { + label: 'Event ID', + type: 'integer', + description: + 'An incrementing counter to distinguish events with the same user ID and timestamp from each other. Amplitude recommends you send an event ID, increasing over time, especially if you expect events to occur simultanenously.' + }, + insert_id: { + label: 'Insert ID', + type: 'string', + description: + 'Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time.' + } +} diff --git a/packages/destination-actions/src/destinations/amplitude/generated-types.ts b/packages/destination-actions/src/destinations/amplitude/generated-types.ts new file mode 100644 index 0000000000..e6b3dd52dc --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Amplitude project API key. You can find this key in the "General" tab of your Amplitude project. + */ + apiKey: string + /** + * Amplitude project secret key. You can find this key in the "General" tab of your Amplitude project. + */ + secretKey: string +} diff --git a/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/generated-types.ts b/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/generated-types.ts new file mode 100644 index 0000000000..5d0103a30b --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/generated-types.ts @@ -0,0 +1,34 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * A UUID (unique user ID) specified by you. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event. Required unless device ID is present. + */ + user_id?: string | null + /** + * A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS. Required unless user ID is present. + */ + device_id?: string + /** + * Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time. + */ + insert_id?: string + /** + * The timestamp of the event. If time is not sent with the event, it will be set to the request upload time. + */ + time?: string + /** + * Additional data tied to the group in Amplitude. + */ + group_properties?: { + [k: string]: unknown + } + /** + * Type of the group + */ + group_type: string + /** + * Value of the group + */ + group_value: string +} diff --git a/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/index.ts b/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/index.ts new file mode 100644 index 0000000000..7c2c06fe34 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/groupIdentifyUser/index.ts @@ -0,0 +1,112 @@ +import { URLSearchParams } from 'url' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import dayjs from '../../../lib/dayjs' + +const action: ActionDefinition = { + title: 'Group Identify User', + description: + 'Set or update properties of particular groups. Note that these updates will only affect events going forward.', + defaultSubscription: 'type = "group"', + fields: { + user_id: { + label: 'User ID', + type: 'string', + allowNull: true, + description: + 'A UUID (unique user ID) specified by you. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event. Required unless device ID is present.', + default: { + '@path': '$.userId' + } + }, + device_id: { + label: 'Device ID', + type: 'string', + description: + 'A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS. Required unless user ID is present.', + default: { + '@if': { + exists: { '@path': '$.context.device.id' }, + then: { '@path': '$.context.device.id' }, + else: { '@path': '$.anonymousId' } + } + } + }, + insert_id: { + label: 'Insert ID', + type: 'string', + description: + 'Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time.' + }, + time: { + label: 'Timestamp', + type: 'string', + description: + 'The timestamp of the event. If time is not sent with the event, it will be set to the request upload time.', + default: { + '@path': '$.timestamp' + } + }, + group_properties: { + label: 'Group Properties', + type: 'object', + description: 'Additional data tied to the group in Amplitude.', + default: { + '@path': '$.traits' + } + }, + group_type: { + label: 'Group Type', + type: 'string', + description: 'Type of the group', + required: true + }, + group_value: { + label: 'Group Value', + type: 'string', + description: 'Value of the group', + required: true + } + }, + perform: async (request, { payload, settings }) => { + const groupAssociation = { [payload.group_type]: payload.group_value } + + // Associate user to group + await request('https://api.amplitude.com/identify', { + method: 'post', + body: new URLSearchParams({ + api_key: settings.apiKey, + identification: JSON.stringify([ + { + device_id: payload.device_id, + groups: groupAssociation, + insert_id: payload.insert_id, + library: 'segment', + time: dayjs.utc(payload.time).valueOf(), + user_id: payload.user_id, + user_properties: groupAssociation + } + ]) + }) + }) + + // Associate group properties + return request('https://api.amplitude.com/groupidentify', { + method: 'post', + body: new URLSearchParams({ + api_key: settings.apiKey, + identification: JSON.stringify([ + { + group_properties: payload.group_properties, + group_value: payload.group_value, + group_type: payload.group_type, + library: 'segment' + } + ]) + }) + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/amplitude/identifyUser/generated-types.ts b/packages/destination-actions/src/destinations/amplitude/identifyUser/generated-types.ts new file mode 100644 index 0000000000..ddd2a7ef8f --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/identifyUser/generated-types.ts @@ -0,0 +1,88 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * A UUID (unique user ID) specified by you. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event. Required unless device ID is present. + */ + user_id?: string | null + /** + * A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS. Required unless user ID is present. + */ + device_id?: string + /** + * Additional data tied to the user in Amplitude. Each distinct value will show up as a user segment on the Amplitude dashboard. Object depth may not exceed 40 layers. **Note:** You can store property values in an array and date values are transformed into string values. + */ + user_properties?: { + [k: string]: unknown + } + /** + * Groups of users for Amplitude's account-level reporting feature. Note: You can only track up to 5 groups. Any groups past that threshold will not be tracked. **Note:** This feature is only available to Amplitude Enterprise customers who have purchased the Amplitude Accounts add-on. + */ + groups?: { + [k: string]: unknown + } + /** + * Version of the app the user is on. + */ + app_version?: string + /** + * What platform is sending the data. + */ + platform?: string + /** + * Mobile operating system or browser the user is on. + */ + os_name?: string + /** + * Version of the mobile operating system or browser the user is on. + */ + os_version?: string + /** + * Device brand the user is on. + */ + device_brand?: string + /** + * Device manufacturer the user is on. + */ + device_manufacturer?: string + /** + * Device model the user is on. + */ + device_model?: string + /** + * Carrier the user has. + */ + carrier?: string + /** + * Country the user is in. + */ + country?: string + /** + * Geographical region the user is in. + */ + region?: string + /** + * What city the user is in. + */ + city?: string + /** + * The Designated Market Area of the user. + */ + dma?: string + /** + * Language the user has set. + */ + language?: string + /** + * Whether the user is paying or not. + */ + paying?: boolean + /** + * Version of the app the user was first on. + */ + start_version?: string + /** + * Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time. + */ + insert_id?: string +} diff --git a/packages/destination-actions/src/destinations/amplitude/identifyUser/index.ts b/packages/destination-actions/src/destinations/amplitude/identifyUser/index.ts new file mode 100644 index 0000000000..c9ee1c27b8 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/identifyUser/index.ts @@ -0,0 +1,179 @@ +import { URLSearchParams } from 'url' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Identify User', + description: + 'Set the user ID for a particular device ID or update user properties without sending an event to Amplitude.', + defaultSubscription: 'type = "identify"', + fields: { + user_id: { + label: 'User ID', + type: 'string', + allowNull: true, + description: + 'A UUID (unique user ID) specified by you. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event. Required unless device ID is present.', + default: { + '@path': '$.userId' + } + }, + device_id: { + label: 'Device ID', + type: 'string', + description: + 'A device specific identifier, such as the Identifier for Vendor (IDFV) on iOS. Required unless user ID is present.', + default: { + '@if': { + exists: { '@path': '$.context.device.id' }, + then: { '@path': '$.context.device.id' }, + else: { '@path': '$.anonymousId' } + } + } + }, + user_properties: { + label: 'User Properties', + type: 'object', + description: + 'Additional data tied to the user in Amplitude. Each distinct value will show up as a user segment on the Amplitude dashboard. Object depth may not exceed 40 layers. **Note:** You can store property values in an array and date values are transformed into string values.', + default: { + '@path': '$.traits' + } + }, + groups: { + label: 'Groups', + type: 'object', + description: + "Groups of users for Amplitude's account-level reporting feature. Note: You can only track up to 5 groups. Any groups past that threshold will not be tracked. **Note:** This feature is only available to Amplitude Enterprise customers who have purchased the Amplitude Accounts add-on." + }, + app_version: { + label: 'App Version', + type: 'string', + description: 'Version of the app the user is on.', + default: { + '@path': '$.context.app.version' + } + }, + platform: { + label: 'Platform', + type: 'string', + description: 'What platform is sending the data.', + default: { + '@path': '$.context.device.type' + } + }, + os_name: { + label: 'OS Name', + type: 'string', + description: 'Mobile operating system or browser the user is on.', + default: { + '@path': '$.context.os.name' + } + }, + os_version: { + label: 'OS Version', + type: 'string', + description: 'Version of the mobile operating system or browser the user is on.', + default: { + '@path': '$.context.os.version' + } + }, + device_brand: { + label: 'Device Brand', + type: 'string', + description: 'Device brand the user is on.', + default: { + '@path': '$.context.device.brand' + } + }, + device_manufacturer: { + label: 'Device Manufacturer', + type: 'string', + description: 'Device manufacturer the user is on.', + default: { + '@path': '$.context.device.manufacturer' + } + }, + device_model: { + label: 'Device Model', + type: 'string', + description: 'Device model the user is on.', + default: { + '@path': '$.context.device.model' + } + }, + carrier: { + label: 'Carrier', + type: 'string', + description: 'Carrier the user has.', + default: { + '@path': '$.context.network.carrier' + } + }, + country: { + label: 'Country', + type: 'string', + description: 'Country the user is in.', + default: { + '@path': '$.context.location.country' + } + }, + region: { + label: 'Region', + type: 'string', + description: 'Geographical region the user is in.', + default: { + '@path': '$.context.location.region' + } + }, + city: { + label: 'City', + type: 'string', + description: 'What city the user is in.', + default: { + '@path': '$.context.location.city' + } + }, + dma: { + label: 'Designated Market Area', + type: 'string', + description: 'The Designated Market Area of the user.' + }, + language: { + label: 'Language', + type: 'string', + description: 'Language the user has set.', + default: { + '@path': '$.context.locale' + } + }, + paying: { + label: 'Is Paying', + type: 'boolean', + description: 'Whether the user is paying or not.' + }, + start_version: { + label: 'Initial Version', + type: 'string', + description: 'Version of the app the user was first on.' + }, + insert_id: { + label: 'Insert ID', + type: 'string', + description: + 'Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time.' + } + }, + perform: (request, { payload, settings }) => { + return request('https://api.amplitude.com/identify', { + method: 'post', + body: new URLSearchParams({ + api_key: settings.apiKey, + identification: JSON.stringify(payload) + }) + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/amplitude/index.ts b/packages/destination-actions/src/destinations/amplitude/index.ts new file mode 100644 index 0000000000..a77e8e3059 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/index.ts @@ -0,0 +1,90 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import { defaultValues } from '@segment/actions-core' +import identifyUser from './identifyUser' +import logEvent from './logEvent' +import mapUser from './mapUser' +import groupIdentifyUser from './groupIdentifyUser' +import type { Settings } from './generated-types' + +/** used in the quick setup */ +const presets: DestinationDefinition['presets'] = [ + { + name: 'Track Calls', + subscribe: 'type = "track"', + partnerAction: 'logEvent', + mapping: defaultValues(logEvent.fields) + }, + { + name: 'Page Calls', + subscribe: 'type = "page"', + partnerAction: 'logEvent', + mapping: { + ...defaultValues(logEvent.fields), + event_type: { + '@template': 'Viewed {{name}}' + } + } + }, + { + name: 'Screen Calls', + subscribe: 'type = "screen"', + partnerAction: 'logEvent', + mapping: { + ...defaultValues(logEvent.fields), + event_type: { + '@template': 'Viewed {{name}}' + } + } + }, + { + name: 'Identify Calls', + subscribe: 'type = "identify"', + partnerAction: 'identifyUser', + mapping: defaultValues(identifyUser.fields) + }, + { + name: 'Browser Session Tracking', + subscribe: 'type = "track" or type = "identify" or type = "group" or type = "page" or type = "alias"', + partnerAction: 'sessionId', + mapping: {} + } +] + +const destination: DestinationDefinition = { + name: 'Amplitude (Actions)', + authentication: { + scheme: 'custom', + fields: { + apiKey: { + label: 'API Key', + description: 'Amplitude project API key. You can find this key in the "General" tab of your Amplitude project.', + type: 'string', + required: true + }, + secretKey: { + label: 'Secret Key', + description: + 'Amplitude project secret key. You can find this key in the "General" tab of your Amplitude project.', + type: 'string', + required: true + } + }, + testAuthentication: (request, { settings }) => { + // Note: Amplitude has some apis that use basic auth (like this one) + // and others that use custom auth in the request body + return request('https://amplitude.com/api/2/usersearch?user=testUser@example.com', { + username: settings.apiKey, + password: settings.secretKey + }) + } + }, + presets, + actions: { + logEvent, + identifyUser, + mapUser, + groupIdentifyUser + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/amplitude/logEvent/generated-types.ts b/packages/destination-actions/src/destinations/amplitude/logEvent/generated-types.ts new file mode 100644 index 0000000000..039dbcf55f --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/logEvent/generated-types.ts @@ -0,0 +1,183 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * When enabled, track revenue with each product within the event. When disabled, track total revenue once for the event. + */ + trackRevenuePerProduct?: boolean + /** + * A readable ID specified by you. Must have a minimum length of 5 characters. Required unless device ID is present. **Note:** If you send a request with a user ID that is not in the Amplitude system yet, then the user tied to that ID will not be marked new until their first event. + */ + user_id?: string | null + /** + * A device-specific identifier, such as the Identifier for Vendor on iOS. Required unless user ID is present. If a device ID is not sent with the event, it will be set to a hashed version of the user ID. + */ + device_id?: string + /** + * A unique identifier for your event. + */ + event_type: string + /** + * The start time of the session, necessary if you want to associate events with a particular system. To use automatic Amplitude session tracking in browsers, enable Analytics 2.0 on your connected source. + */ + session_id?: string | number + /** + * The timestamp of the event. If time is not sent with the event, it will be set to the request upload time. + */ + time?: string | number + /** + * An object of key-value pairs that represent additional data to be sent along with the event. You can store property values in an array, but note that Amplitude only supports one-dimensional arrays. Date values are transformed into string values. Object depth may not exceed 40 layers. + */ + event_properties?: { + [k: string]: unknown + } + /** + * An object of key-value pairs that represent additional data tied to the user. You can store property values in an array, but note that Amplitude only supports one-dimensional arrays. Date values are transformed into string values. Object depth may not exceed 40 layers. + */ + user_properties?: { + [k: string]: unknown + } + /** + * Groups of users for the event as an event-level group. You can only track up to 5 groups. **Note:** This Amplitude feature is only available to Enterprise customers who have purchased the Accounts add-on. + */ + groups?: { + [k: string]: unknown + } + /** + * The current version of your application. + */ + app_version?: string + /** + * Platform of the device. + */ + platform?: string + /** + * The name of the mobile operating system or browser that the user is using. + */ + os_name?: string + /** + * The version of the mobile operating system or browser the user is using. + */ + os_version?: string + /** + * The device brand that the user is using. + */ + device_brand?: string + /** + * The device manufacturer that the user is using. + */ + device_manufacturer?: string + /** + * The device model that the user is using. + */ + device_model?: string + /** + * The carrier that the user is using. + */ + carrier?: string + /** + * The current country of the user. + */ + country?: string + /** + * The current region of the user. + */ + region?: string + /** + * The current city of the user. + */ + city?: string + /** + * The current Designated Market Area of the user. + */ + dma?: string + /** + * The language set by the user. + */ + language?: string + /** + * The price of the item purchased. Required for revenue data if the revenue field is not sent. You can use negative values to indicate refunds. + */ + price?: number + /** + * The quantity of the item purchased. Defaults to 1 if not specified. + */ + quantity?: number + /** + * Revenue = price * quantity. If you send all 3 fields of price, quantity, and revenue, then (price * quantity) will be used as the revenue value. You can use negative values to indicate refunds. **Note:** You will need to explicitly set this if you are using the Amplitude in cloud-mode. + */ + revenue?: number + /** + * An identifier for the item purchased. You must send a price and quantity or revenue with this field. + */ + productId?: string + /** + * The type of revenue for the item purchased. You must send a price and quantity or revenue with this field. + */ + revenueType?: string + /** + * The current Latitude of the user. + */ + location_lat?: number + /** + * The current Longitude of the user. + */ + location_lng?: number + /** + * The IP address of the user. Use "$remote" to use the IP address on the upload request. Amplitude will use the IP address to reverse lookup a user's location (city, country, region, and DMA). Amplitude has the ability to drop the location and IP address from events once it reaches our servers. You can submit a request to Amplitude's platform specialist team here to configure this for you. + */ + ip?: string + /** + * Identifier for Advertiser. _(iOS)_ + */ + idfa?: string + /** + * Identifier for Vendor. _(iOS)_ + */ + idfv?: string + /** + * Google Play Services advertising ID. _(Android)_ + */ + adid?: string + /** + * Android ID (not the advertising ID). _(Android)_ + */ + android_id?: string + /** + * An incrementing counter to distinguish events with the same user ID and timestamp from each other. Amplitude recommends you send an event ID, increasing over time, especially if you expect events to occur simultanenously. + */ + event_id?: number + /** + * Amplitude will deduplicate subsequent events sent with this ID we have already seen before within the past 7 days. Amplitude recommends generating a UUID or using some combination of device ID, user ID, event type, event ID, and time. + */ + insert_id?: string + /** + * The list of products purchased. + */ + products?: { + /** + * The price of the item purchased. Required for revenue data if the revenue field is not sent. You can use negative values to indicate refunds. + */ + price?: number + /** + * The quantity of the item purchased. Defaults to 1 if not specified. + */ + quantity?: number + /** + * Revenue = price * quantity. If you send all 3 fields of price, quantity, and revenue, then (price * quantity) will be used as the revenue value. You can use negative values to indicate refunds. + */ + revenue?: number + /** + * An identifier for the item purchased. You must send a price and quantity or revenue with this field. + */ + productId?: string + /** + * The type of revenue for the item purchased. You must send a price and quantity or revenue with this field. + */ + revenueType?: string + }[] + /** + * If true, events are sent to Amplitude's `batch` endpoint rather than their `httpapi` events endpoint. Enabling this setting may help reduce 429s – or throttling errors – from Amplitude. More information about Amplitude's throttling is available in [their docs](https://developers.amplitude.com/docs/batch-event-upload-api#429s-in-depth). + */ + use_batch_endpoint?: boolean +} diff --git a/packages/destination-actions/src/destinations/amplitude/logEvent/index.ts b/packages/destination-actions/src/destinations/amplitude/logEvent/index.ts new file mode 100644 index 0000000000..ee6a428ea2 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/logEvent/index.ts @@ -0,0 +1,144 @@ +import { omit } from '@segment/actions-core' +import dayjs from '../../../lib/dayjs' +import { eventSchema } from '../event-schema' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +interface AmplitudeEvent extends Omit { + time?: number + session_id?: number +} + +const revenueKeys = ['revenue', 'price', 'productId', 'quantity', 'revenueType'] + +interface EventRevenue { + revenue?: number + price?: number + productId?: string + quantity?: number + revenueType?: string +} + +function getRevenueProperties(payload: EventRevenue): EventRevenue { + if (typeof payload.revenue !== 'number') { + return {} + } + + return { + revenue: payload.revenue, + revenueType: payload.revenueType ?? 'Purchase', + quantity: typeof payload.quantity === 'number' ? Math.round(payload.quantity) : undefined, + price: payload.price, + productId: payload.productId + } +} + +const action: ActionDefinition = { + title: 'Log Event', + description: 'Send an event to Amplitude.', + defaultSubscription: 'type = "track"', + fields: { + trackRevenuePerProduct: { + label: 'Track Revenue Per Product', + description: + 'When enabled, track revenue with each product within the event. When disabled, track total revenue once for the event.', + type: 'boolean', + default: false + }, + ...eventSchema, + products: { + label: 'Products', + description: 'The list of products purchased.', + type: 'object', + multiple: true, + properties: { + price: { + label: 'Price', + type: 'number', + description: + 'The price of the item purchased. Required for revenue data if the revenue field is not sent. You can use negative values to indicate refunds.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'The quantity of the item purchased. Defaults to 1 if not specified.' + }, + revenue: { + label: 'Revenue', + type: 'number', + description: + 'Revenue = price * quantity. If you send all 3 fields of price, quantity, and revenue, then (price * quantity) will be used as the revenue value. You can use negative values to indicate refunds.' + }, + productId: { + label: 'Product ID', + type: 'string', + description: + 'An identifier for the item purchased. You must send a price and quantity or revenue with this field.' + }, + revenueType: { + label: 'Revenue Type', + type: 'string', + description: + 'The type of revenue for the item purchased. You must send a price and quantity or revenue with this field.' + } + }, + default: { + '@path': '$.properties.products' + } + }, + use_batch_endpoint: { + label: 'Use Batch Endpoint', + description: + "If true, events are sent to Amplitude's `batch` endpoint rather than their `httpapi` events endpoint. Enabling this setting may help reduce 429s – or throttling errors – from Amplitude. More information about Amplitude's throttling is available in [their docs](https://developers.amplitude.com/docs/batch-event-upload-api#429s-in-depth).", + type: 'boolean', + default: false + } + }, + perform: (request, { payload, settings }) => { + // Omit revenue properties initially because we will manually stitch those into events as prescribed + const { products = [], trackRevenuePerProduct, time, session_id, ...rest } = omit(payload, revenueKeys) + const properties = rest as AmplitudeEvent + + if (time && dayjs.utc(time).isValid()) { + properties.time = dayjs.utc(time).valueOf() + } + + if (session_id && dayjs.utc(session_id).isValid()) { + properties.session_id = dayjs.utc(session_id).valueOf() + } + + const events: AmplitudeEvent[] = [ + { + ...properties, + // Conditionally track revenue with main event + ...(products.length && trackRevenuePerProduct ? {} : getRevenueProperties(payload)) + } + ] + + for (const product of products) { + events.push({ + ...properties, + // Or track revenue per product + ...(trackRevenuePerProduct ? getRevenueProperties(product as EventRevenue) : {}), + event_properties: product, + event_type: 'Product Purchased', + insert_id: properties.insert_id ? `${properties.insert_id}-${events.length + 1}` : undefined + }) + } + + const endpoint = payload.use_batch_endpoint + ? 'https://api2.amplitude.com/batch' + : 'https://api2.amplitude.com/2/httpapi' + + return request(endpoint, { + method: 'post', + json: { + api_key: settings.apiKey, + events + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/amplitude/mapUser/generated-types.ts b/packages/destination-actions/src/destinations/amplitude/mapUser/generated-types.ts new file mode 100644 index 0000000000..970e6d72ce --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/mapUser/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The User ID to be associated + */ + user_id?: string + /** + * The global User ID to associate to + */ + global_user_id?: string +} diff --git a/packages/destination-actions/src/destinations/amplitude/mapUser/index.ts b/packages/destination-actions/src/destinations/amplitude/mapUser/index.ts new file mode 100644 index 0000000000..578e8748e0 --- /dev/null +++ b/packages/destination-actions/src/destinations/amplitude/mapUser/index.ts @@ -0,0 +1,39 @@ +import { URLSearchParams } from 'url' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Map User', + description: 'Merge two users together that would otherwise have different User IDs tracked in Amplitude.', + defaultSubscription: 'type = "alias"', + fields: { + user_id: { + label: 'User ID', + type: 'string', + description: 'The User ID to be associated', + default: { + '@path': '$.previousId' + } + }, + global_user_id: { + label: 'Global User ID', + type: 'string', + description: 'The global User ID to associate to', + default: { + '@path': '$.userId' + } + } + }, + perform: (request, { payload, settings }) => { + return request('https://api.amplitude.com/usermap', { + method: 'post', + body: new URLSearchParams({ + api_key: settings.apiKey, + mapping: JSON.stringify([payload]) + }) + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/customerio/createUpdateDevice/generated-types.ts b/packages/destination-actions/src/destinations/customerio/createUpdateDevice/generated-types.ts new file mode 100644 index 0000000000..40e11ff5e1 --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/createUpdateDevice/generated-types.ts @@ -0,0 +1,20 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * ID of the person that this device belongs to. + */ + person_id: string + /** + * Unique ID for this device. + */ + device_id: string + /** + * The device platform. + */ + platform: string + /** + * Timestamp for when the device was last used. Default is current date and time. + */ + last_used?: string +} diff --git a/packages/destination-actions/src/destinations/customerio/createUpdateDevice/index.ts b/packages/destination-actions/src/destinations/customerio/createUpdateDevice/index.ts new file mode 100644 index 0000000000..81c575057b --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/createUpdateDevice/index.ts @@ -0,0 +1,63 @@ +import dayjs from '../../../lib/dayjs' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Create or Update Device', + description: "Update a person's device in Customer.io or create it if it doesn't exist.", + defaultSubscription: 'type = "track" and event = "Application Installed"', + fields: { + person_id: { + label: 'Person ID', + description: 'ID of the person that this device belongs to.', + type: 'string', + required: true, + default: { + '@path': '$.userId' + } + }, + device_id: { + label: 'Device ID', + description: 'Unique ID for this device.', + type: 'string', + required: true, + default: { + '@path': '$.context.device.id' + } + }, + platform: { + label: 'Platform', + description: 'The device platform.', + type: 'string', + required: true, + // enum: ['ios', 'android'], + default: { + '@path': '$.context.device.type' + } + }, + last_used: { + label: 'Last Used', + description: 'Timestamp for when the device was last used. Default is current date and time.', + type: 'string', + default: { + '@path': '$.timestamp' + } + } + }, + + perform: (request, { payload }) => { + return request(`https://track.customer.io/api/v1/customers/${payload.person_id}/devices`, { + method: 'put', + json: { + device: { + id: payload.device_id, + platform: payload.platform, + last_used: payload.last_used ? dayjs.utc(payload.last_used).format('X') : undefined + } + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/customerio/createUpdatePerson/generated-types.ts b/packages/destination-actions/src/destinations/customerio/createUpdatePerson/generated-types.ts new file mode 100644 index 0000000000..5d129e0341 --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/createUpdatePerson/generated-types.ts @@ -0,0 +1,22 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * ID used to uniquely identify person in Customer.io. + */ + id: string + /** + * Person's email address. + */ + email: string + /** + * Timestamp for when the person was created. Default is current date and time. + */ + created_at?: string + /** + * Optional custom attributes for this person. When updating a person, attributes are added and not removed. + */ + custom_attributes?: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/customerio/createUpdatePerson/index.ts b/packages/destination-actions/src/destinations/customerio/createUpdatePerson/index.ts new file mode 100644 index 0000000000..f288c8499f --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/createUpdatePerson/index.ts @@ -0,0 +1,60 @@ +import dayjs from '../../../lib/dayjs' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Create or Update Person', + description: "Update a person in Customer.io or create them if they don't exist.", + defaultSubscription: 'type = "identify"', + fields: { + id: { + label: 'Person ID', + description: 'ID used to uniquely identify person in Customer.io.', + type: 'string', + required: true, + default: { + '@path': '$.userId' + } + }, + email: { + label: 'Email Address', + description: "Person's email address.", + type: 'string', + required: true, + default: { + '@template': '{{traits.userId}}' + } + }, + created_at: { + label: 'Created At', + description: 'Timestamp for when the person was created. Default is current date and time.', + type: 'string', + default: { + '@path': '$.timestamp' + } + }, + custom_attributes: { + label: 'Custom Attributes', + description: + 'Optional custom attributes for this person. When updating a person, attributes are added and not removed.', + type: 'object', + default: { + '@path': '$.traits' + } + } + }, + + perform: (request, { payload }) => { + return request(`https://track.customer.io/api/v1/customers/${payload.id}`, { + method: 'put', + json: { + ...payload.custom_attributes, + email: payload.email, + created_at: payload.created_at ? dayjs.utc(payload.created_at).format('X') : undefined + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/customerio/generated-types.ts b/packages/destination-actions/src/destinations/customerio/generated-types.ts new file mode 100644 index 0000000000..6be92f1f5f --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Customer.io site ID. This can be found on your [API Credentials page](https://fly.customer.io/settings/api_credentials). + */ + siteId: string + /** + * Customer.io API key. This can be found on your [API Credentials page](https://fly.customer.io/settings/api_credentials). + */ + apiKey: string +} diff --git a/packages/destination-actions/src/destinations/customerio/index.ts b/packages/destination-actions/src/destinations/customerio/index.ts new file mode 100644 index 0000000000..581ca01102 --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/index.ts @@ -0,0 +1,74 @@ +import { defaultValues } from '@segment/actions-core' +import createUpdateDevice from './createUpdateDevice' +import createUpdatePerson from './createUpdatePerson' +import trackAnonymousEvent from './trackAnonymousEvent' +import trackEvent from './trackEvent' +import triggerCampaign from './triggerCampaign' +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: 'Customer.io', + authentication: { + scheme: 'basic', + fields: { + siteId: { + description: + 'Customer.io site ID. This can be found on your [API Credentials page](https://fly.customer.io/settings/api_credentials).', + // minLength: 20, + label: 'Site ID', + type: 'string', + required: true + }, + apiKey: { + description: + 'Customer.io API key. This can be found on your [API Credentials page](https://fly.customer.io/settings/api_credentials).', + // minLength: 20, + label: 'API Key', + type: 'string', + required: true + } + }, + testAuthentication: (request) => { + return request('https://track.customer.io/auth') + } + }, + + extendRequest({ settings }) { + return { + username: settings.siteId, + password: settings.apiKey + } + }, + + actions: { + createUpdateDevice, + createUpdatePerson, + trackAnonymousEvent, + trackEvent, + triggerCampaign + }, + + presets: [ + { + name: 'Create or Update Person', + subscribe: 'type = "identify"', + partnerAction: 'createUpdatePerson', + mapping: defaultValues(createUpdatePerson.fields) + }, + { + name: 'Create or Update Device', + subscribe: 'type = "track" and event = "Application Installed"', + partnerAction: 'createUpdateDevice', + mapping: defaultValues(createUpdateDevice.fields) + }, + { + name: 'Track Event', + subscribe: 'type = "track"', + partnerAction: 'trackEvent', + mapping: defaultValues(trackEvent.fields) + } + ] +} + +export default destination diff --git a/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/generated-types.ts b/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/generated-types.ts new file mode 100644 index 0000000000..18f7e6bfe9 --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/generated-types.ts @@ -0,0 +1,14 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Name of the event + */ + name: string + /** + * Custom data to include with the event. If "recipient", "from_address", or "reply_to" are sent, they will override settings on any campaigns triggered by this event. "recipient" is required if the event is used to trigger a campaign. + */ + data?: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/index.ts b/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/index.ts new file mode 100644 index 0000000000..2b12391b4b --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/trackAnonymousEvent/index.ts @@ -0,0 +1,38 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Track Anonymous Event', + description: 'Track an event not tied to a known person.', + defaultSubscription: 'type = "track"', + fields: { + name: { + label: 'Event Name', + description: 'Name of the event', + type: 'string', + required: true, + default: { + '@path': '$.event' + } + }, + data: { + label: 'Data', + description: + 'Custom data to include with the event. If "recipient", "from_address", or "reply_to" are sent, they will override settings on any campaigns triggered by this event. "recipient" is required if the event is used to trigger a campaign.', + type: 'object', + default: { + '@path': '$.properties' + } + } + }, + + perform: (request, { payload }) => { + return request('https://track.customer.io/api/v1/events', { + method: 'post', + json: payload + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/customerio/trackEvent/generated-types.ts b/packages/destination-actions/src/destinations/customerio/trackEvent/generated-types.ts new file mode 100644 index 0000000000..bc163d007c --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/trackEvent/generated-types.ts @@ -0,0 +1,22 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * ID of the person who triggered this event. + */ + id: string + /** + * Name of the event + */ + name: string + /** + * Override event type. Ex. "page". + */ + type?: string + /** + * Custom data to include with the event. + */ + data?: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/customerio/trackEvent/index.ts b/packages/destination-actions/src/destinations/customerio/trackEvent/index.ts new file mode 100644 index 0000000000..62df4069fe --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/trackEvent/index.ts @@ -0,0 +1,58 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Track Event', + description: 'Track an event for a known person.', + defaultSubscription: 'type = "track"', + fields: { + id: { + label: 'Person ID', + description: 'ID of the person who triggered this event.', + type: 'string', + required: true, + default: { + '@path': '$.userId' + } + }, + name: { + label: 'Event Name', + description: 'Name of the event', + type: 'string', + required: true, + default: { + '@path': '$.event' + } + }, + type: { + label: 'Event Type', + description: 'Override event type. Ex. "page".', + type: 'string', + default: { + '@path': '$.type' + } + }, + data: { + label: 'Data', + description: 'Custom data to include with the event.', + type: 'object', + default: { + '@path': '$.properties' + } + } + }, + + perform: (request, { payload }) => { + return request(`https://track.customer.io/api/v1/customers/${payload.id}/events`, { + method: 'post', + json: { + name: payload.name, + type: payload.type, + data: payload.data + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/customerio/triggerCampaign/generated-types.ts b/packages/destination-actions/src/destinations/customerio/triggerCampaign/generated-types.ts new file mode 100644 index 0000000000..89742cdc67 --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/triggerCampaign/generated-types.ts @@ -0,0 +1,24 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * ID of the campaign to trigger. + */ + id: number + /** + * Custom Liquid merge data to include with the trigger. + */ + data?: { + [k: string]: unknown + } + /** + * Additional recipient conditions to filter recipients. If this is used, "IDs" may not be used. + */ + recipients?: { + [k: string]: unknown + } + /** + * List of profile IDs to use as campaign recipients. If this is used, "Recipients" may not be used. + */ + ids?: string[] +} diff --git a/packages/destination-actions/src/destinations/customerio/triggerCampaign/index.ts b/packages/destination-actions/src/destinations/customerio/triggerCampaign/index.ts new file mode 100644 index 0000000000..fe67aa7fbe --- /dev/null +++ b/packages/destination-actions/src/destinations/customerio/triggerCampaign/index.ts @@ -0,0 +1,83 @@ +import type { ActionDefinition, DynamicFieldResponse, RequestFn } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +interface Campaigns { + campaigns: Campaign[] +} + +interface Campaign { + id: string + name: string +} + +const idAutocomplete: RequestFn = async (request, { settings }) => { + const response = await request('https://beta-api.customer.io/v1/api/campaigns', { + username: settings.siteId, + password: settings.apiKey + }) + + const items = response.data.campaigns.map((campaign) => ({ + label: campaign.name, + value: campaign.id + })) + + return { + body: { + data: items, + pagination: {} + } + } +} + +const action: ActionDefinition = { + title: 'Trigger Broadcast Campaign', + description: 'Trigger a Customer.io broadcast campaign.', + // Hide so we can delete later. No customers currently using it. + hidden: true, + fields: { + id: { + label: 'Campaign ID', + description: 'ID of the campaign to trigger.', + type: 'number', + required: true, + dynamic: true + }, + data: { + label: 'Data', + description: 'Custom Liquid merge data to include with the trigger.', + type: 'object', + default: { + '@path': '$.properties' + } + }, + recipients: { + label: 'Recipients', + description: 'Additional recipient conditions to filter recipients. If this is used, "IDs" may not be used.', + type: 'object' + }, + ids: { + label: 'Profile IDs', + description: 'List of profile IDs to use as campaign recipients. If this is used, "Recipients" may not be used.', + type: 'string', + multiple: true + } + }, + + dynamicFields: { + id: idAutocomplete + }, + + perform: (request, { payload }) => { + return request(`https://api.customer.io/v1/api/campaigns/${payload.id}/triggers`, { + method: 'post', + json: { + ids: payload.ids, + data: payload.data, + recipients: payload.recipients + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/addToCart.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/addToCart.test.ts new file mode 100644 index 0000000000..cec6ddd326 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/addToCart.test.ts @@ -0,0 +1,236 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Add to Cart', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Added', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + cart_id: 'skdjsidjsdkdj29j', + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + position: 3, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('addToCart', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + value: { + '@path': '$.properties.price' + }, + items: [ + { + item_name: { + '@path': `$.properties.name` + }, + item_id: { + '@path': `$.properties.product_id` + }, + quantity: { + '@path': `$.properties.quantity` + }, + coupon: { + '@path': `$.properties.coupon` + }, + item_brand: { + '@path': `$.properties..brand` + }, + item_category: { + '@path': `$.properties.category` + }, + item_variant: { + '@path': `$.properties.variant` + }, + price: { + '@path': `$.properties.price` + } + } + ] + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"add_to_cart\\",\\"params\\":{\\"items\\":[{\\"item_name\\":\\"Monopoly: 3rd Edition\\",\\"item_id\\":\\"507f1f77bcf86cd799439011\\",\\"quantity\\":1,\\"coupon\\":\\"MAYDEALS\\",\\"item_brand\\":\\"Hasbro\\",\\"item_category\\":\\"Games\\",\\"item_variant\\":\\"200 pieces\\",\\"price\\":18.99}],\\"value\\":18.99}}]}"` + ) + }) + + it('should throw an error when product name and id are missing', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Added', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + category: 'Games', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + position: 3, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + + try { + await testDestination.testAction('addToCart', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + value: { + '@path': '$.properties.price' + }, + items: [ + { + item_name: { + '@path': `$.properties.name` + }, + item_id: { + '@path': `$.properties.product_id` + }, + quantity: { + '@path': `$.properties.quantity` + }, + coupon: { + '@path': `$.properties.coupon` + }, + item_brand: { + '@path': `$.properties..brand` + }, + item_category: { + '@path': `$.properties.category` + }, + item_variant: { + '@path': `$.properties.variant` + }, + price: { + '@path': `$.properties.price` + } + } + ] + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of product name or product id is required for product or impression data.') + } + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Added', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + cart_id: 'skdjsidjsdkdj29j', + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + position: 3, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('addToCart', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"add_to_cart\\",\\"params\\":{\\"items\\":[]}}]}"` + ) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/beginCheckout.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/beginCheckout.test.ts new file mode 100644 index 0000000000..03bc702cbd --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/beginCheckout.test.ts @@ -0,0 +1,258 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Begin Checkout', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Checkout Started', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + order_id: '50314b8e9bcf000000000000', + affiliation: 'Google Store', + value: 30, + revenue: 25.0, + shipping: 3, + tax: 2, + discount: 2.5, + coupon: 'hasbros', + currency: 'USD', + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: 19, + quantity: 1, + category: 'Games', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + ] + } + }) + const responses = await testDestination.testAction('beginCheckout', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + currency: { + '@path': '$.properties.currency' + }, + value: { + '@path': '$.properties.revenue' + }, + items: [ + { + item_name: { + '@path': `$.properties.products.0.name` + }, + item_category: { + '@path': `$.properties.products.0.category` + } + } + ] + }, + useDefaultMappings: false + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"begin_checkout\\",\\"params\\":{\\"coupon\\":\\"hasbros\\",\\"currency\\":\\"USD\\",\\"items\\":[{\\"item_name\\":\\"Monopoly: 3rd Edition\\",\\"item_category\\":\\"Games\\"}],\\"value\\":25}}]}"` + ) + }) + + it('should throw an error for invalid currency values', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Checkout Started', + userId: '3456fff', + type: 'track', + properties: { + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: '1234' + } + }) + try { + await testDestination.testAction('beginCheckout', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.userId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + currency: { + '@path': '$.properties.currency' + } + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('1234 is not a valid currency code.') + } + }) + + it('should throw an error for products missing name and id', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Checkout Started', + userId: '3456fff', + type: 'track', + properties: { + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'USD', + products: [ + { + quantity: 2, + coupon: 'MOUNTAIN', + brand: 'Canvas', + category: 'T-Shirt', + variant: 'Black', + price: 19.98 + } + ] + } + }) + try { + await testDestination.testAction('beginCheckout', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + currency: { + '@path': '$.properties.currency' + }, + value: { + '@path': '$.properties.value' + }, + items: [ + { + item_brand: { + '@path': `$.properties.brand` + } + } + ] + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of product name or product id is required for product or impression data.') + } + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Checkout Started', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + order_id: '50314b8e9bcf000000000000', + affiliation: 'Google Store', + value: 30, + revenue: 25.0, + shipping: 3, + tax: 2, + discount: 2.5, + coupon: 'hasbros', + currency: 'USD', + products: [ + { + product_id: '507f1f77bcf86cd799439011', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + price: 19, + quantity: 1, + category: 'Games', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + ] + } + }) + const responses = await testDestination.testAction('beginCheckout', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"begin_checkout\\",\\"params\\":{\\"coupon\\":\\"hasbros\\",\\"currency\\":\\"USD\\",\\"items\\":[],\\"value\\":30}}]}"` + ) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/customEvent.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/customEvent.test.ts new file mode 100644 index 0000000000..454f0b8271 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/customEvent.test.ts @@ -0,0 +1,175 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Custom Event', () => { + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + affiliation: 'TI Online Store', + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'EUR', + products: [ + { + product_id: 'pid-123456', + sku: 'SKU-123456', + name: 'Tour t-shirt', + quantity: 2, + coupon: 'MOUNTAIN', + brand: 'Canvas', + category: 'T-Shirt', + variant: 'Black', + price: 19.98 + } + ] + } + }) + const responses = await testDestination.testAction('customEvent', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + name: 'this_is_a_test' + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"this_is_a_test\\",\\"params\\":{\\"affiliation\\":\\"TI Online Store\\",\\"order_id\\":\\"5678dd9087-78\\",\\"coupon\\":\\"SUMMER_FEST\\",\\"currency\\":\\"EUR\\",\\"products\\":[{\\"product_id\\":\\"pid-123456\\",\\"sku\\":\\"SKU-123456\\",\\"name\\":\\"Tour t-shirt\\",\\"quantity\\":2,\\"coupon\\":\\"MOUNTAIN\\",\\"brand\\":\\"Canvas\\",\\"category\\":\\"T-Shirt\\",\\"variant\\":\\"Black\\",\\"price\\":19.98}]}}]}"` + ) + }) + + it('should succeed without any parameters provided', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track' + }) + const responses = await testDestination.testAction('customEvent', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + name: 'this_is_a_test' + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"this_is_a_test\\",\\"params\\":{}}]}"` + ) + }) + + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + custom_props: { + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'USD', + revenue: 11.99, + total: 15.99 + }, + tax: 55 + } + }) + const responses = await testDestination.testAction('customEvent', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + name: 'this_is_a_test', + params: { + '@path': '$.properties.custom_props' + } + }, + useDefaultMappings: false + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"this_is_a_test\\",\\"params\\":{\\"order_id\\":\\"5678dd9087-78\\",\\"coupon\\":\\"SUMMER_FEST\\",\\"currency\\":\\"USD\\",\\"revenue\\":11.99,\\"total\\":15.99}}]}"` + ) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/pageView.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/pageView.test.ts new file mode 100644 index 0000000000..55f0a7acca --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/pageView.test.ts @@ -0,0 +1,127 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Page View', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Page View', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + custom_url: 'http://www.example.com/pageOne', + title: 'Analytics Academy', + url: 'http://www.example.com/pageOne' + }, + context: { + page: { + path: '/pageOne', + referrer: 'https://segment.com/academy/', + search: '', + title: 'Analytics Academy', + url: 'http://www.example.com/home' + } + } + }) + const responses = await testDestination.testAction('pageView', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + page_location: { + '@path': '$.properties.custom_url' + } + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"page_view\\",\\"params\\":{\\"page_location\\":\\"http://www.example.com/pageOne\\",\\"page_referrer\\":\\"https://segment.com/academy/\\"}}]}"` + ) + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Page View', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + custom_url: 'http://www.example.com/pageOne', + title: 'Analytics Academy', + url: 'http://www.example.com/pageOne' + }, + context: { + page: { + path: '/pageOne', + referrer: 'https://segment.com/academy/', + search: '', + title: 'Analytics Academy', + url: 'http://www.example.com/home' + } + } + }) + const responses = await testDestination.testAction('pageView', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"page_view\\",\\"params\\":{\\"page_location\\":\\"http://www.example.com/home\\",\\"page_referrer\\":\\"https://segment.com/academy/\\"}}]}"` + ) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/purchase.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/purchase.test.ts new file mode 100644 index 0000000000..6b6d05f6b6 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/purchase.test.ts @@ -0,0 +1,316 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Purchase', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + affiliation: 'TI Online Store', + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'EUR', + products: [ + { + product_id: 'pid-123456', + sku: 'SKU-123456', + name: 'Tour t-shirt', + quantity: 2, + coupon: 'MOUNTAIN', + brand: 'Canvas', + category: 'T-Shirt', + variant: 'Black', + price: 19.98 + } + ], + revenue: 5.99, + shipping: 1.5, + tax: 3.0, + total: 24.48 + } + }) + const responses = await testDestination.testAction('purchase', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + currency: { + '@path': '$.properties.currency' + }, + transaction_id: { + '@path': '$.properties.order_id' + }, + value: { + '@path': '$.properties.revenue' + }, + items: [ + { + item_name: { + '@path': `$.properties.products.0.name` + }, + item_id: { + '@path': `$.properties.products.0.product_id` + }, + quantity: { + '@path': `$.properties.products.0.quantity` + }, + coupon: { + '@path': `$.properties.products.0.coupon` + }, + item_brand: { + '@path': `$.properties.products.0.brand` + }, + item_category: { + '@path': `$.properties.products.0.category` + }, + item_variant: { + '@path': `$.properties.products.0.variant` + }, + price: { + '@path': `$.properties.products.0.price` + } + } + ] + }, + useDefaultMappings: false + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"purchase\\",\\"params\\":{\\"coupon\\":\\"SUMMER_FEST\\",\\"currency\\":\\"EUR\\",\\"items\\":[{\\"item_name\\":\\"Tour t-shirt\\",\\"item_id\\":\\"pid-123456\\",\\"quantity\\":2,\\"coupon\\":\\"MOUNTAIN\\",\\"item_brand\\":\\"Canvas\\",\\"item_category\\":\\"T-Shirt\\",\\"item_variant\\":\\"Black\\",\\"price\\":19.98}],\\"transaction_id\\":\\"5678dd9087-78\\",\\"value\\":5.99}}]}"` + ) + }) + + it('should throw an error for invalid currency values', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + type: 'track', + properties: { + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: '1234' + } + }) + try { + await testDestination.testAction('purchase', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.userId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + currency: { + '@path': '$.properties.currency' + }, + transaction_id: { + '@path': '$.properties.order_id' + } + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('1234 is not a valid currency code.') + } + }) + + it('should throw error when product name and id are missing', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + affiliation: 'TI Online Store', + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'EUR', + products: [ + { + quantity: 2, + coupon: 'MOUNTAIN', + brand: 'Canvas', + category: 'T-Shirt', + variant: 'Black', + price: 19.98 + } + ], + revenue: 5.99, + shipping: 1.5, + tax: 3.0, + total: 24.48 + } + }) + + try { + await testDestination.testAction('purchase', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + coupon: { + '@path': '$.properties.coupon' + }, + currency: { + '@path': '$.properties.currency' + }, + transaction_id: { + '@path': '$.properties.order_id' + }, + value: { + '@path': '$.properties.revenue' + }, + items: [ + { + item_name: { + '@path': `$.properties.products.0.name` + }, + item_id: { + '@path': `$.properties.products.0.product_id` + }, + quantity: { + '@path': `$.properties.products.0.quantity` + }, + coupon: { + '@path': `$.properties.products.0.coupon` + }, + item_brand: { + '@path': `$.properties.products.0.brand` + }, + item_category: { + '@path': `$.properties.products.0.category` + }, + item_variant: { + '@path': `$.properties.products.0.variant` + }, + price: { + '@path': `$.properties.products.0.price` + } + } + ] + }, + useDefaultMappings: false + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of product name or product id is required for product or impression data.') + } + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Order Completed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + affiliation: 'TI Online Store', + order_id: '5678dd9087-78', + coupon: 'SUMMER_FEST', + currency: 'EUR', + products: [ + { + product_id: 'pid-123456', + sku: 'SKU-123456', + name: 'Tour t-shirt', + quantity: 2, + coupon: 'MOUNTAIN', + brand: 'Canvas', + category: 'T-Shirt', + variant: 'Black', + price: 19.98 + } + ], + revenue: 5.99, + shipping: 1.5, + tax: 3.0, + total: 24.48 + } + }) + const responses = await testDestination.testAction('purchase', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"purchase\\",\\"params\\":{\\"affiliation\\":\\"TI Online Store\\",\\"coupon\\":\\"SUMMER_FEST\\",\\"currency\\":\\"EUR\\",\\"items\\":[],\\"transaction_id\\":\\"5678dd9087-78\\",\\"shipping\\":1.5,\\"value\\":24.48,\\"tax\\":3}}]}"` + ) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectItem.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectItem.test.ts new file mode 100644 index 0000000000..919a054829 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectItem.test.ts @@ -0,0 +1,206 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Select Item', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + position: 3, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('selectItem', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + items: [ + { + item_id: { + '@path': `$.properties.product_id` + }, + item_name: { + '@path': `$.properties.name` + }, + item_category: { + '@path': `$.properties.category` + }, + quantity: { + '@path': `$.properties.quantity` + }, + coupon: { + '@path': `$.properties.coupon` + }, + index: { + '@path': `$.properties.position` + }, + item_brand: { + '@path': `$.properties.brand` + }, + item_variant: { + '@path': `$.properties.variant` + }, + price: { + '@path': `$.properties.price` + } + } + ] + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"select_item\\",\\"params\\":{\\"items\\":[{\\"item_id\\":\\"507f1f77bcf86cd799439011\\",\\"item_name\\":\\"Monopoly: 3rd Edition\\",\\"item_category\\":\\"Games\\",\\"quantity\\":1,\\"coupon\\":\\"MAYDEALS\\",\\"index\\":3,\\"item_brand\\":\\"Hasbro\\",\\"item_variant\\":\\"200 pieces\\",\\"price\\":18.99}]}}]}"` + ) + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '5678fkj9087', + sku: 'U-567890', + category: 'Clothing', + name: 'Limited Edition T', + brand: 'yeezy', + variant: 'Black', + price: 8.99, + quantity: 1, + coupon: 'SummerFest', + position: 30, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('selectItem', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"select_item\\",\\"params\\":{\\"items\\":[]}}]}"` + ) + }) + + it('should throw an error for products missing name and id', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '5678fkj9087', + sku: 'U-567890', + category: 'Clothing', + name: 'Limited Edition T', + brand: 'yeezy', + variant: 'Black', + price: 8.99, + quantity: 1, + coupon: 'SummerFest', + position: 30, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + try { + await testDestination.testAction('selectItem', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + items: [ + { + item_brand: { + '@path': `$.properties.brand` + } + } + ] + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of product name or product id is required for product or impression data.') + } + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectPromotion.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectPromotion.test.ts new file mode 100644 index 0000000000..6b1cf7780d --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/selectPromotion.test.ts @@ -0,0 +1,319 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('Select Promotion', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Promotion Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + promotion_id: 'promo_1', + creative: 'top_banner_2', + name: '75% store-wide shoe sale', + position: 'home_banner_top' + } + }) + const responses = await testDestination.testAction('selectPromotion', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + location_id: { + '@path': '$.properties.promotion_id' + } + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"select_promotion\\",\\"params\\":{\\"location_id\\":\\"promo_1\\",\\"items\\":[]}}]}"` + ) + }) + + it('should handle complete mapping', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Promotion Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + promotion_id: 'promo_1', + creative: 'top_banner_2', + name: '75% store-wide shoe sale', + position: 'home_banner_top', + items: [ + { + item_id: 'SKU_12345', + item_name: 'jeggings', + coupon: 'SUMMER_FUN', + discount: 2.22, + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + affiliation: 'Google Store', + item_brand: 'Gucci', + item_category: 'pants', + item_variant: 'Black', + price: 9.99, + currency: 'USD' + } + ] + } + }) + const responses = await testDestination.testAction('selectPromotion', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + location_id: { + '@path': '$.properties.promotion_id' + }, + items: { + '@path': '$.properties.items' + } + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"select_promotion\\",\\"params\\":{\\"location_id\\":\\"promo_1\\",\\"items\\":[{\\"item_id\\":\\"SKU_12345\\",\\"item_name\\":\\"jeggings\\",\\"coupon\\":\\"SUMMER_FUN\\",\\"discount\\":2.22,\\"promotion_id\\":\\"P_12345\\",\\"promotion_name\\":\\"Summer Sale\\",\\"creative_slot\\":\\"featured_app_1\\",\\"location_id\\":\\"L_12345\\",\\"affiliation\\":\\"Google Store\\",\\"item_brand\\":\\"Gucci\\",\\"item_category\\":\\"pants\\",\\"item_variant\\":\\"Black\\",\\"price\\":9.99,\\"currency\\":\\"USD\\"}]}}]}"` + ) + }) + + it('should throw error when product name and id is missing', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Promotion Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + promotion_id: 'promo_1', + creative: 'top_banner_2', + name: '75% store-wide shoe sale', + position: 'home_banner_top', + items: [ + { + coupon: 'SUMMER_FUN', + discount: 2.22, + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + affiliation: 'Google Store', + item_brand: 'Gucci', + item_category: 'pants', + item_variant: 'Black', + price: 9.99, + currency: 'USD' + } + ] + } + }) + + try { + await testDestination.testAction('selectPromotion', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + location_id: { + '@path': '$.properties.promotion_id' + }, + items: { + '@path': '$.properties.items' + } + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of product name or product id is required for product or impression data.') + } + }) + + it('should throw error when promotion name and id is missing', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Promotion Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + promotion_id: 'promo_1', + creative: 'top_banner_2', + name: '75% store-wide shoe sale', + position: 'home_banner_top', + items: [ + { + item_id: 'SKU_12345', + item_name: 'jeggings', + coupon: 'SUMMER_FUN', + discount: 2.22, + creative_slot: 'featured_app_1', + location_id: 'L_12345', + affiliation: 'Google Store', + item_brand: 'Gucci', + item_category: 'pants', + item_variant: 'Black', + price: 9.99, + currency: 'USD' + } + ] + } + }) + + try { + await testDestination.testAction('selectPromotion', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + location_id: { + '@path': '$.properties.promotion_id' + }, + items: { + '@path': '$.properties.items' + } + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('One of promotion name or promotion id is required.') + } + }) + + it('should throw error when item currency is invalid', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Promotion Clicked', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + promotion_id: 'promo_1', + creative: 'top_banner_2', + name: '75% store-wide shoe sale', + position: 'home_banner_top', + items: [ + { + item_id: 'SKU_12345', + item_name: 'jeggings', + coupon: 'SUMMER_FUN', + discount: 2.22, + promotion_id: 'P_12345', + promotion_name: 'Summer Sale', + creative_slot: 'featured_app_1', + location_id: 'L_12345', + affiliation: 'Google Store', + item_brand: 'Gucci', + item_category: 'pants', + item_variant: 'Black', + price: 9.99, + currency: 'US4D' + } + ] + } + }) + + try { + await testDestination.testAction('selectPromotion', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + clientId: { + '@path': '$.anonymousId' + }, + location_id: { + '@path': '$.properties.promotion_id' + }, + items: { + '@path': '$.properties.items' + } + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('US4D is not a valid currency code.') + } + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/__tests__/viewItem.test.ts b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/viewItem.test.ts new file mode 100644 index 0000000000..cfd3b42299 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/__tests__/viewItem.test.ts @@ -0,0 +1,182 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import ga4 from '../index' + +const testDestination = createTestIntegration(ga4) +const apiSecret = 'b287432uhkjHIUEL' +const measurementId = 'G-TESTTOKEN' + +describe('GA4', () => { + describe('View Item', () => { + it('should handle basic mapping overrides', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Viewed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + currency: 'USD', + position: 3, + value: 18.99, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('viewItem', { + event, + settings: { + apiSecret, + measurementId + }, + mapping: { + client_id: { + '@path': '$.anonymousId' + }, + currency: { + '@path': '$.properties.currency' + }, + value: { + '@path': '$.properties.value' + }, + items: [ + { + item_id: { + '@path': `$.properties.name` + } + } + ] + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"anon-567890\\",\\"events\\":[{\\"name\\":\\"view_item\\",\\"params\\":{\\"currency\\":\\"USD\\",\\"items\\":[{\\"item_id\\":\\"Monopoly: 3rd Edition\\"}],\\"value\\":18.99}}]}"` + ) + }) + + it('should handle default mappings', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Viewed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + currency: 'USD', + position: 3, + value: 18.99, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + const responses = await testDestination.testAction('viewItem', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "content-type": Array [ + "application/json", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"client_id\\":\\"3456fff\\",\\"events\\":[{\\"name\\":\\"view_item\\",\\"params\\":{\\"currency\\":\\"USD\\",\\"items\\":[],\\"value\\":18.99}}]}"` + ) + }) + + it('should throw an error when provided invalid currency', async () => { + nock('https://www.google-analytics.com/mp/collect') + .post(`?measurement_id=${measurementId}&api_secret=${apiSecret}`) + .reply(201, {}) + const event = createTestEvent({ + event: 'Product Viewed', + userId: '3456fff', + anonymousId: 'anon-567890', + type: 'track', + properties: { + product_id: '507f1f77bcf86cd799439011', + sku: 'G-32', + category: 'Games', + name: 'Monopoly: 3rd Edition', + brand: 'Hasbro', + variant: '200 pieces', + price: 18.99, + quantity: 1, + coupon: 'MAYDEALS', + currency: '1234', + position: 3, + value: 18.99, + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg' + } + }) + try { + await testDestination.testAction('viewItem', { + event, + settings: { + apiSecret, + measurementId + }, + useDefaultMappings: true + }) + fail('the test should have thrown an error') + } catch (e) { + expect(e.message).toBe('1234 is not a valid currency code.') + } + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-analytics-4/addToCart/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/addToCart/generated-types.ts new file mode 100644 index 0000000000..9ebf42b102 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/addToCart/generated-types.ts @@ -0,0 +1,77 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + /** + * The list of products in the event. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * A product affiliation to designate a supplying company or brick and mortar store location. + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * The index of the item in a list. + */ + index?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Category of the product. + */ + item_category?: string + /** + * The name of the list in which the item was presented to the user. + */ + item_list_name?: string + /** + * The ID of the list in which the item was presented to the user. + */ + item_list_id?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] + /** + * The monetary value of the event, in units of the specified currency parameter. + */ + value?: number +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/addToCart/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/addToCart/index.ts new file mode 100644 index 0000000000..f8ce2e18b6 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/addToCart/index.ts @@ -0,0 +1,157 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { CartProductItem } from '../ga4-types' +import { CURRENCY_ISO_CODES } from '../constants' + +const action: ActionDefinition = { + title: 'Add to Cart', + description: 'Send product added events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Product Added"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + }, + items: { + label: 'Products', + description: 'The list of products in the event.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'A product affiliation to designate a supplying company or brick and mortar store location.' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + index: { + label: 'Index', + type: 'integer', + description: 'The index of the item in a list.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Category of the product.' + }, + item_list_name: { + label: 'Item List Name', + type: 'string', + description: 'The name of the list in which the item was presented to the user.' + }, + item_list_id: { + label: 'Item List Name', + type: 'string', + description: 'The ID of the list in which the item was presented to the user.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + }, + value: { + label: 'Value', + type: 'number', + description: 'The monetary value of the event, in units of the specified currency parameter.', + default: { + '@path': '$.properties.value' + } + } + }, + perform: (request, { payload }) => { + let googleItems: CartProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + return product as CartProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'add_to_cart', + params: { + currency: payload.currency, + items: googleItems, + value: payload.value + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/generated-types.ts new file mode 100644 index 0000000000..f1db21114f --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/generated-types.ts @@ -0,0 +1,69 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + /** + * The list of products purchased. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * A product affiliation to designate a supplying company or brick and mortar store location. + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Product category. + */ + item_category?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] + /** + * The monetary value of the event, in units of the specified currency parameter. + */ + value?: number +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/index.ts new file mode 100644 index 0000000000..385244493a --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/beginCheckout/index.ts @@ -0,0 +1,160 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import { CURRENCY_ISO_CODES } from '../constants' +import { ProductItem } from '../ga4-types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Begin Checkout', + description: 'Send begin checkout events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Checkout Started"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.', + default: { + '@path': '$.properties.coupon' + } + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.', + default: { + '@path': '$.properties.currency' + } + }, + // Google does not have anything to map position, url and image url fields (Segment spec) to + // so will ignore for now + items: { + label: 'Products', + description: 'The list of products purchased.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'A product affiliation to designate a supplying company or brick and mortar store location.' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Product category.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + }, + value: { + label: 'Value', + type: 'number', + description: 'The monetary value of the event, in units of the specified currency parameter.', + default: { + '@path': '$.properties.value' + } + } + }, + perform: (request, { payload }) => { + if (payload.currency && !CURRENCY_ISO_CODES.includes(payload.currency)) { + throw new IntegrationError(`${payload.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + let googleItems: ProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + return product as ProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'begin_checkout', + params: { + coupon: payload.coupon, + currency: payload.currency, + items: googleItems, + value: payload.value + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/constants.ts b/packages/destination-actions/src/destinations/google-analytics-4/constants.ts new file mode 100644 index 0000000000..248af59b5e --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/constants.ts @@ -0,0 +1,181 @@ +export const CURRENCY_ISO_CODES = [ + 'AED', + 'AFN', + 'ALL', + 'AMD', + 'ANG', + 'AOA', + 'ARS', + 'AUD', + 'AWG', + 'AZN', + 'BAM', + 'BBD', + 'BDT', + 'BGN', + 'BHD', + 'BIF', + 'BMD', + 'BND', + 'BOB', + 'BOV', + 'BRL', + 'BSD', + 'BTN', + 'BWP', + 'BYN', + 'BZD', + 'CAD', + 'CDF', + 'CHE', + 'CHF', + 'CHW', + 'CLF', + 'CLP', + 'CNY', + 'COP', + 'COU', + 'CRC', + 'CUC', + 'CUP', + 'CVE', + 'CZK', + 'DJF', + 'DKK', + 'DOP', + 'DZD', + 'EGP', + 'ERN', + 'ETB', + 'EUR', + 'FJD', + 'FKP', + 'GBP', + 'GEL', + 'GHS', + 'GIP', + 'GMD', + 'GNF', + 'GTQ', + 'GYD', + 'HKD', + 'HNL', + 'HRK', + 'HTG', + 'HUF', + 'IDR', + 'ILS', + 'INR', + 'IQD', + 'IRR', + 'ISK', + 'JMD', + 'JOD', + 'JPY', + 'KES', + 'KGS', + 'KHR', + 'KMF', + 'KPW', + 'KRW', + 'KWD', + 'KYD', + 'KZT', + 'LAK', + 'LBP', + 'LKR', + 'LRD', + 'LSL', + 'LYD', + 'MAD', + 'MDL', + 'MGA', + 'MKD', + 'MMK', + 'MNT', + 'MOP', + 'MRU', + 'MUR', + 'MVR', + 'MWK', + 'MXN', + 'MXV', + 'MYR', + 'MZN', + 'NAD', + 'NGN', + 'NIO', + 'NOK', + 'NPR', + 'NZD', + 'OMR', + 'PAB', + 'PEN', + 'PGK', + 'PHP', + 'PKR', + 'PLN', + 'PYG', + 'QAR', + 'RON', + 'RSD', + 'RUB', + 'RWF', + 'SAR', + 'SBD', + 'SCR', + 'SDG', + 'SEK', + 'SGD', + 'SHP', + 'SLL', + 'SOS', + 'SRD', + 'SSP', + 'STN', + 'SVC', + 'SYP', + 'SZL', + 'THB', + 'TJS', + 'TMT', + 'TND', + 'TOP', + 'TRY', + 'TTD', + 'TWD', + 'TZS', + 'UAH', + 'UGX', + 'USD', + 'USN', + 'UYI', + 'UYU', + 'UYW', + 'UZS', + 'VES', + 'VND', + 'VUV', + 'WST', + 'XAF', + 'XAG', + 'XAU', + 'XBA', + 'XBB', + 'XBC', + 'XBD', + 'XCD', + 'XDR', + 'XOF', + 'XPD', + 'XPF', + 'XPT', + 'XSU', + 'XTS', + 'XUA', + 'XXX', + 'YER', + 'ZAR', + 'ZMW', + 'ZWC' +] diff --git a/packages/destination-actions/src/destinations/google-analytics-4/customEvent/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/customEvent/generated-types.ts new file mode 100644 index 0000000000..b567bbf076 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/customEvent/generated-types.ts @@ -0,0 +1,18 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + clientId: string + /** + * The unique name of the custom event created in GA4. + */ + name: string + /** + * The event parameters to send to Google + */ + params?: { + [k: string]: unknown + } +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/customEvent/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/customEvent/index.ts new file mode 100644 index 0000000000..d393446cf1 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/customEvent/index.ts @@ -0,0 +1,52 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Send a Custom Event', + description: 'Send events to a custom event in GA4', + defaultSubscription: 'type = "track"', + fields: { + clientId: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + name: { + label: 'Event Name', + description: 'The unique name of the custom event created in GA4.', + type: 'string', + required: true + }, + params: { + label: 'Event Parameters', + description: 'The event parameters to send to Google', + type: 'object', + default: { '@path': '$.properties' } + } + }, + perform: (request, { payload }) => { + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.clientId, + events: [ + { + name: payload.name, + params: payload.params + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/ga4-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/ga4-types.ts new file mode 100644 index 0000000000..0484ea53d2 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/ga4-types.ts @@ -0,0 +1,18 @@ +export interface ProductItem { + item_id?: string + item_name?: string + quantity?: number + affiliation?: string + coupon?: string + discount?: number + item_brand?: string + item_category?: string + item_variant?: string + tax?: number + price?: number + currency?: string +} + +export interface CartProductItem extends ProductItem { + index?: number +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/generated-types.ts new file mode 100644 index 0000000000..5d98cc375d --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * The measurement ID associated with a stream. Found in the Google Analytics UI under: Admin > Data Streams > choose your stream > Measurement ID + */ + measurementId: string + /** + * An API SECRET generated in the Google Analytics UI, navigate to: Admin > Data Streams > choose your stream > Measurement Protocol > Create + */ + apiSecret: string +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/index.ts new file mode 100644 index 0000000000..f473d0f23f --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/index.ts @@ -0,0 +1,58 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' +import purchase from './purchase' +import addToCart from './addToCart' +import pageView from './pageView' +import customEvent from './customEvent' +import selectItem from './selectItem' +import beginCheckout from './beginCheckout' +import selectPromotion from './selectPromotion' +import viewItem from './viewItem' + +const destination: DestinationDefinition = { + name: 'Google Analytics 4', + authentication: { + scheme: 'custom', + fields: { + measurementId: { + label: 'Measurement ID', + description: + 'The measurement ID associated with a stream. Found in the Google Analytics UI under: Admin > Data Streams > choose your stream > Measurement ID', + type: 'string', + required: true + }, + apiSecret: { + label: 'API Secret', + description: + 'An API SECRET generated in the Google Analytics UI, navigate to: Admin > Data Streams > choose your stream > Measurement Protocol > Create', + type: 'string', + required: true + } + }, + testAuthentication: (_request) => { + // Return a request that tests/validates the user's authentication fields here + // TODO: maybe run a post to the google measurements protocol debug endpoint + return true + } + }, + extendRequest({ settings }) { + return { + searchParams: { + measurement_id: settings.measurementId, + api_secret: settings.apiSecret + } + } + }, + actions: { + purchase, + addToCart, + pageView, + customEvent, + selectItem, + beginCheckout, + selectPromotion, + viewItem + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/google-analytics-4/pageView/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/pageView/generated-types.ts new file mode 100644 index 0000000000..8c9505f2e8 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/pageView/generated-types.ts @@ -0,0 +1,16 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + clientId: string + /** + * The current page URL + */ + page_location?: string + /** + * Previous page URL + */ + page_referrer?: string +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/pageView/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/pageView/index.ts new file mode 100644 index 0000000000..aea9629442 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/pageView/index.ts @@ -0,0 +1,59 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Page View', + description: 'Send page view events to GA4 to make the most of the recommended event reports in Google Analytics', + defaultSubscription: 'type = "page"', + fields: { + clientId: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + page_location: { + label: 'Page Location', + type: 'string', + description: 'The current page URL', + default: { + '@path': '$.context.page.url' + } + }, + page_referrer: { + label: 'Page Referrer', + type: 'string', + description: 'Previous page URL', + default: { + '@path': '$.context.page.referrer' + } + } + }, + perform: (request, { payload }) => { + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.clientId, + events: [ + { + name: 'page_view', + params: { + page_location: payload.page_location, + page_referrer: payload.page_referrer + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/purchase/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/purchase/generated-types.ts new file mode 100644 index 0000000000..b139277181 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/purchase/generated-types.ts @@ -0,0 +1,89 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * Store or affiliation from which this transaction occurred (e.g. Google Store). + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency: string + /** + * The list of products purchased. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * Store or affiliation from which this transaction occurred (e.g. Google Store). + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Product category. + */ + item_category?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * Total tax associated with the transaction. + */ + tax?: number + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] + /** + * The unique identifier of a transaction. + */ + transaction_id: string + /** + * Shipping cost associated with the transaction. + */ + shipping?: number + /** + * Total tax associated with the transaction. + */ + tax?: number + /** + * The monetary value of the event, in units of the specified currency parameter. + */ + value?: number +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/purchase/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/purchase/index.ts new file mode 100644 index 0000000000..29b6aa5d7d --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/purchase/index.ts @@ -0,0 +1,205 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { CURRENCY_ISO_CODES } from '../constants' +import { ProductItem } from '../ga4-types' + +// https://segment.com/docs/connections/spec/ecommerce/v2/#order-completed +// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#purchase +const action: ActionDefinition = { + title: 'Purchase', + description: 'Send purchase events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Order Completed"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'Store or affiliation from which this transaction occurred (e.g. Google Store).', + default: { + '@path': '$.properties.affiliation' + } + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.', + default: { + '@path': '$.properties.coupon' + } + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.', + required: true, + default: { + '@path': '$.properties.currency' + } + }, + // Google does not have anything to map position, url and image url fields (Segment spec) to + // so will ignore for now + items: { + label: 'Products', + description: 'The list of products purchased.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'Store or affiliation from which this transaction occurred (e.g. Google Store).' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Product category.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + tax: { + label: 'Tax', + type: 'number', + description: 'Total tax associated with the transaction.' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + }, + transaction_id: { + label: 'Order Id', + type: 'string', + description: 'The unique identifier of a transaction.', + required: true, + default: { + '@path': '$.properties.order_id' + } + }, + shipping: { + label: 'Shipping', + type: 'number', + description: 'Shipping cost associated with the transaction.', + default: { + '@path': '$.properties.shipping' + } + }, + tax: { + label: 'Tax', + type: 'number', + description: 'Total tax associated with the transaction.', + default: { + '@path': '$.properties.tax' + } + }, + value: { + label: 'Value', + type: 'number', + description: 'The monetary value of the event, in units of the specified currency parameter.', + default: { + '@path': '$.properties.total' + } + } + }, + perform: (request, { payload }) => { + if (!CURRENCY_ISO_CODES.includes(payload.currency)) { + throw new Error(`${payload.currency} is not a valid currency code.`) + } + + let googleItems: ProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + return product as ProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'purchase', + params: { + affiliation: payload.affiliation, + coupon: payload.coupon, + currency: payload.currency, + items: googleItems, + transaction_id: payload.transaction_id, + shipping: payload.shipping, + value: payload.value, + tax: payload.tax + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/selectItem/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/selectItem/generated-types.ts new file mode 100644 index 0000000000..292d238eb8 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/selectItem/generated-types.ts @@ -0,0 +1,77 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * The name of the list in which the item was presented to the user. + */ + item_list_name?: string + /** + * The ID of the list in which the item was presented to the user. + */ + item_list_id?: string + /** + * The list of products in the event. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * A product affiliation to designate a supplying company or brick and mortar store location. + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * The index of the item in a list. + */ + index?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Category of the product. + */ + item_category?: string + /** + * The name of the list in which the item was presented to the user. + */ + item_list_name?: string + /** + * The ID of the list in which the item was presented to the user. + */ + item_list_id?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/selectItem/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/selectItem/index.ts new file mode 100644 index 0000000000..a625aa44be --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/selectItem/index.ts @@ -0,0 +1,154 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import { CURRENCY_ISO_CODES } from '../constants' +import { CartProductItem } from '../ga4-types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Select Item', + description: 'Send select item events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Product Clicked"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + item_list_name: { + label: 'Item List Name', + description: 'The name of the list in which the item was presented to the user.', + type: 'string' + }, + item_list_id: { + label: 'Item List Id', + description: 'The ID of the list in which the item was presented to the user.', + type: 'string' + }, + items: { + label: 'Products', + description: 'The list of products in the event.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'A product affiliation to designate a supplying company or brick and mortar store location.' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + index: { + label: 'Index', + type: 'integer', + description: 'The index of the item in a list.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Category of the product.' + }, + item_list_name: { + label: 'Item List Name', + type: 'string', + description: 'The name of the list in which the item was presented to the user.' + }, + item_list_id: { + label: 'Item List Name', + type: 'string', + description: 'The ID of the list in which the item was presented to the user.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + } + }, + perform: (request, { payload }) => { + let googleItems: CartProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + return product as CartProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'select_item', + params: { + items: googleItems, + item_list_name: payload.item_list_name, + item_list_id: payload.item_list_id + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/generated-types.ts new file mode 100644 index 0000000000..0b5fe77369 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/generated-types.ts @@ -0,0 +1,81 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * The ID of the location. + */ + location_id?: string + /** + * The list of products in the event. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * The ID of a product promotion. + */ + promotion_id?: string + /** + * The name of a product promotion. + */ + promotion_name?: string + /** + * A product affiliation to designate a supplying company or brick and mortar store location. + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * The name of a creative used in a promotional spot. + */ + creative_name?: string + /** + * The name of a creative slot. + */ + creative_slot?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Category of the product. + */ + item_category?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * The location associated with the event. If possible, set to the Google Place ID that corresponds to the associated item. Can also be overridden to a custom location ID string. + */ + location_id?: string + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/index.ts new file mode 100644 index 0000000000..8fde5e4d53 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/selectPromotion/index.ts @@ -0,0 +1,167 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import { CURRENCY_ISO_CODES } from '../constants' +import { ProductItem } from '../ga4-types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Select Promotion', + description: 'Send select promotion events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Promotion Clicked"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + location_id: { + label: 'Location ID', + type: 'string', + description: 'The ID of the location.' + }, + items: { + label: 'Products', + description: 'The list of products in the event.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + promotion_id: { + label: 'Promotion ID', + type: 'string', + description: 'The ID of a product promotion.' + }, + promotion_name: { + label: 'Promotion Name', + type: 'string', + description: 'The name of a product promotion.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'A product affiliation to designate a supplying company or brick and mortar store location.' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + creative_name: { + label: 'Creative Name', + type: 'string', + description: 'The name of a creative used in a promotional spot.' + }, + creative_slot: { + label: 'Creative Slot', + type: 'string', + description: 'The name of a creative slot.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Category of the product.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + location_id: { + label: 'Location ID', + type: 'string', + description: + 'The location associated with the event. If possible, set to the Google Place ID that corresponds to the associated item. Can also be overridden to a custom location ID string.' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + } + }, + perform: (request, { payload }) => { + let googleItems: ProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + if (product.promotion_id === undefined && product.promotion_name === undefined) { + throw new IntegrationError( + 'One of promotion name or promotion id is required.', + 'Misconfigured required field', + 400 + ) + } + + return product as ProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'select_promotion', + params: { + location_id: payload.location_id, + items: googleItems + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-analytics-4/viewItem/generated-types.ts b/packages/destination-actions/src/destinations/google-analytics-4/viewItem/generated-types.ts new file mode 100644 index 0000000000..6344e3f67a --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/viewItem/generated-types.ts @@ -0,0 +1,65 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Uniquely identifies a user instance of a web client. + */ + client_id: string + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + /** + * The monetary value of the event, in units of the specified currency parameter. + */ + value?: number + /** + * The list of products in the event. + */ + items?: { + /** + * Identifier for the product being purchased. + */ + item_id?: string + /** + * Name of the product being purchased. + */ + item_name?: string + /** + * Item quantity. + */ + quantity?: number + /** + * A product affiliation to designate a supplying company or brick and mortar store location. + */ + affiliation?: string + /** + * Coupon code used for a purchase. + */ + coupon?: string + /** + * Monetary value of discount associated with a purchase. + */ + discount?: number + /** + * Brand associated with the product. + */ + item_brand?: string + /** + * Category of the product. + */ + item_category?: string + /** + * Variant of the product (e.g. Black). + */ + item_variant?: string + /** + * Price of the product being purchased, in units of the specified currency parameter. + */ + price?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency?: string + }[] +} diff --git a/packages/destination-actions/src/destinations/google-analytics-4/viewItem/index.ts b/packages/destination-actions/src/destinations/google-analytics-4/viewItem/index.ts new file mode 100644 index 0000000000..f152e6e724 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-analytics-4/viewItem/index.ts @@ -0,0 +1,161 @@ +import { ActionDefinition, IntegrationError } from '@segment/actions-core' +import { CURRENCY_ISO_CODES } from '../constants' +import { ProductItem } from '../ga4-types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'View Item', + description: 'Send view item events to GA4 to make the most of the ecommerce reports in Google Analytics', + defaultSubscription: 'type = "track" and event = "Product Viewed"', + fields: { + client_id: { + label: 'Client ID', + description: 'Uniquely identifies a user instance of a web client.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.userId' }, + then: { '@path': '$.userId' }, + else: { '@path': '$.anonymousId' } + } + } + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.', + default: { + '@path': '$.properties.currency' + } + }, + value: { + label: 'Value', + type: 'number', + description: 'The monetary value of the event, in units of the specified currency parameter.', + default: { + '@path': '$.properties.value' + } + }, + items: { + label: 'Products', + description: 'The list of products in the event.', + type: 'object', + multiple: true, + properties: { + item_id: { + label: 'Product ID', + type: 'string', + description: 'Identifier for the product being purchased.' + }, + item_name: { + label: 'Name', + type: 'string', + description: 'Name of the product being purchased.' + }, + quantity: { + label: 'Quantity', + type: 'integer', + description: 'Item quantity.' + }, + affiliation: { + label: 'Affiliation', + type: 'string', + description: 'A product affiliation to designate a supplying company or brick and mortar store location.' + }, + coupon: { + label: 'Coupon', + type: 'string', + description: 'Coupon code used for a purchase.' + }, + discount: { + label: 'Discount', + type: 'number', + description: 'Monetary value of discount associated with a purchase.' + }, + item_brand: { + label: 'Brand', + type: 'string', + description: 'Brand associated with the product.' + }, + item_category: { + label: 'Category', + type: 'string', + description: 'Category of the product.' + }, + item_variant: { + label: 'Variant', + type: 'string', + description: 'Variant of the product (e.g. Black).' + }, + price: { + label: 'Price', + type: 'number', + description: 'Price of the product being purchased, in units of the specified currency parameter.' + }, + currency: { + label: 'Currency', + type: 'string', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.' + } + } + } + }, + perform: (request, { payload }) => { + if (payload.currency && !CURRENCY_ISO_CODES.includes(payload.currency)) { + throw new IntegrationError(`${payload.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + let googleItems: ProductItem[] = [] + + if (payload.items) { + googleItems = payload.items.map((product) => { + if (product.item_name === undefined && product.item_id === undefined) { + throw new IntegrationError( + 'One of product name or product id is required for product or impression data.', + 'Misconfigured required field', + 400 + ) + } + + if (product.currency && !CURRENCY_ISO_CODES.includes(product.currency)) { + throw new IntegrationError(`${product.currency} is not a valid currency code.`, 'Incorrect value format', 400) + } + + return { + item_id: product.item_id, + item_name: product.item_name, + quantity: product.quantity, + affiliation: product.affiliation, + coupon: product.coupon, + discount: product.discount, + item_brand: product.item_brand, + item_category: product.item_category, + item_variant: product.item_variant, + price: product.price, + currency: product.currency + } as ProductItem + }) + } + + return request('https://www.google-analytics.com/mp/collect', { + method: 'POST', + json: { + client_id: payload.client_id, + events: [ + { + name: 'view_item', + params: { + currency: payload.currency, + items: googleItems, + value: payload.value + } + } + ] + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/google-enhanced-conversions.test.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/google-enhanced-conversions.test.ts new file mode 100644 index 0000000000..d3532fa3ed --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/__tests__/google-enhanced-conversions.test.ts @@ -0,0 +1,57 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import GoogleEnhancedConversions from '../index' + +const testDestination = createTestIntegration(GoogleEnhancedConversions) +const timestamp = new Date('Thu Jun 10 2021 11:08:04 GMT-0700 (Pacific Daylight Time)').toISOString() +const conversionTrackingId = '_conversion_id_' +const conversionLabel = '_conversion_' + +describe('GoogleEnhancedConversions', () => { + describe('postConversion', () => { + it('should should send an event with default mappings', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + properties: { + email: 'janedoe@gmail.com', + orderId: '123', + firstName: 'Bob John', + lastName: 'Smith', + phone: '14150000000', + address: { + street: '123 Market Street', + city: 'San Francisco', + state: 'CA', + postalCode: '94000', + country: 'USA' + } + } + }) + + nock('https://www.google.com/ads/event/api/v1') + .post(`?conversion_tracking_id=${conversionTrackingId}`) + .reply(201, {}) + + const responses = await testDestination.testAction('postConversion', { + event, + mapping: { conversion_label: conversionLabel }, + useDefaultMappings: true, + settings: { conversionTrackingId } + }) + + expect(responses[0].options.body).toMatchInlineSnapshot( + `"{\\"pii_data\\":{\\"hashed_email\\":\\"1hFzBkhe0OUK+rOshx6Y+BaZFR8wKBUn1j/18jNlbGk=\\",\\"hashed_phone_number\\":[\\"5pAiami9y4LWCmP12H9fXJpoqrnOFRL7u9q1pkqlMmI=\\"],\\"address\\":[{\\"hashed_first_name\\":\\"IGT0sXMskUo9vWuqGeOhA+RylOG2Oj/IcIX2Zr5f7GU=\\",\\"hashed_last_name\\":\\"ZieDX5iOLF5QUz1JEWMHLT9PQfXIsEYwFQ3rs3Isot0=\\",\\"hashed_street_address\\":\\"tHP71r8+GY59XKpmdb6ssI3fd7TIBB6E6aCWN06RGBw=\\",\\"city\\":\\"sanfrancisco\\",\\"region\\":\\"ca\\",\\"postcode\\":\\"94000\\",\\"country\\":\\"USA\\"}]},\\"oid\\":\\"123\\",\\"user_agent\\":\\"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\\",\\"conversion_time\\":1623348484000000,\\"label\\":\\"_conversion_\\"}"` + ) + + expect(responses[0].options.searchParams).toMatchInlineSnapshot(` + Object { + "conversion_tracking_id": "_conversion_id_", + } + `) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/generated-types.ts new file mode 100644 index 0000000000..40f01d0325 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/generated-types.ts @@ -0,0 +1,8 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Tracking id that uniquely identifies your advertiser account. + */ + conversionTrackingId: string +} diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/index.ts new file mode 100644 index 0000000000..e4c8cb8488 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/index.ts @@ -0,0 +1,35 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +import postConversion from './postConversion' + +const destination: DestinationDefinition = { + name: 'Actions Google Enhanced Conversions', + authentication: { + scheme: 'custom', + fields: { + conversionTrackingId: { + label: 'Conversion Tracking ID', + description: 'Tracking id that uniquely identifies your advertiser account.', + type: 'string', + required: true + } + }, + testAuthentication: () => { + // TODO: Return a request that tests/validates the user's authentication fields here + return true + } + }, + extendRequest({ settings }) { + return { + searchParams: { + conversion_tracking_id: settings.conversionTrackingId + } + } + }, + actions: { + postConversion + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/formatter.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/formatter.ts new file mode 100644 index 0000000000..b667c4a985 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/formatter.ts @@ -0,0 +1,51 @@ +import { createHash } from 'crypto' + +export function formatEmail(email: String): String { + // Emails must be all lowercase. + let lowerCaseEmail = email.toLowerCase() + + // We have to remove periods before the '@' for all @gmail.com and @googlemail.com email addresses. + if (lowerCaseEmail.indexOf('gmail') > -1 || lowerCaseEmail.indexOf('googlemail')) { + const [name, domain] = lowerCaseEmail.split('@') + const nameNoPeriods = name.replace(/\./g, '') + lowerCaseEmail = `${nameNoPeriods}@${domain}` + } + + return createHash('sha256').update(lowerCaseEmail).digest('base64') +} + +export function formatPhone(phone?: String): String { + if (!phone) return '' + return createHash('sha256') + .update(`+${phone.replace(/[^0-9]/g, '')}`) + .digest('base64') +} + +export function formatFirstName(firstName?: String): String { + if (!firstName) return '' + return createHash('sha256') + .update(firstName.toLowerCase().replace(/[^a-z]/g, '')) + .digest('base64') +} + +export function formatLastName(lastName?: String): String { + if (!lastName) return '' + return createHash('sha256') + .update(lastName.toLowerCase().replace(/[^a-z]/g, '')) + .digest('base64') +} + +export function formatStreet(street?: String): String { + if (!street) return '' + return createHash('sha256').update(street.toLowerCase()).digest('base64') +} + +export function formatCity(city?: String): String { + if (!city) return '' + return city.toLowerCase().replace(/[^a-z]/g, '') +} + +export function formatRegion(region?: String): String { + if (!region) return '' + return region.toLowerCase().replace(/[^a-z]/g, '') +} diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/generated-types.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/generated-types.ts new file mode 100644 index 0000000000..bc381431ba --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/generated-types.ts @@ -0,0 +1,64 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The Google Ads conversion label. You can find this value from your Google Ads event snippet. The provided event snippet should have, for example, `send_to: AW-123456789/AbC-D_efG-h12_34-567`. Enter the part after the forward slash, without the AW- prefix, e.g. 123456789 + */ + conversion_label: string + /** + * Email address of the customer who triggered the conversion event. + */ + email: string + /** + * Order ID of the conversion event. Google requires an Order ID even if the event is not an ecommerce event. + */ + transaction_id: string + /** + * User Agent of the customer who triggered the conversion event. + */ + user_agent: string + /** + * Timestamp of the conversion event. + */ + conversion_time: string + /** + * The monetary value attributed to the conversion event. + */ + value?: number + /** + * Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format. + */ + currency_code?: string + /** + * Phone number of the purchaser, in E.164 standard format, e.g. +14150000000 + */ + phone_number?: string + /** + * First name of the individual who triggered the conversion event. + */ + first_name?: string + /** + * Last name of the individual who triggered the conversion event. + */ + last_name?: string + /** + * Street address of the individual who triggered the conversion event. + */ + street_address?: string + /** + * City of the individual who triggered the conversion event. + */ + city?: string + /** + * Region of the individual who triggered the conversion event. + */ + region?: string + /** + * Post code of the individual who triggered the conversion event. + */ + post_code?: string + /** + * Country of the individual who triggered the conversion event. + */ + country?: string +} diff --git a/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts new file mode 100644 index 0000000000..25c78766e8 --- /dev/null +++ b/packages/destination-actions/src/destinations/google-enhanced-conversions/postConversion/index.ts @@ -0,0 +1,229 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import { + formatCity, + formatEmail, + formatFirstName, + formatLastName, + formatPhone, + formatStreet, + formatRegion +} from './formatter' +import { pickBy } from 'lodash' + +const action: ActionDefinition = { + title: 'Post Conversion', + description: 'Send a conversion event to Google Ads.', + fields: { + // Required Fields - These fields are required by Google's EC API to successfully match conversions. + conversion_label: { + label: 'Conversion Label', + description: + 'The Google Ads conversion label. You can find this value from your Google Ads event snippet. The provided event snippet should have, for example, `send_to: AW-123456789/AbC-D_efG-h12_34-567`. Enter the part after the forward slash, without the AW- prefix, e.g. 123456789', + type: 'string', + required: true, + default: '' + }, + email: { + label: 'Email', + description: 'Email address of the customer who triggered the conversion event.', + type: 'string', + required: true, + default: { + '@if': { + exists: { '@path': '$.properties.email' }, + then: { '@path': '$.properties.email' }, + else: { '@path': '$.traits.email' } + } + } + }, + transaction_id: { + label: 'Order ID', + description: + 'Order ID of the conversion event. Google requires an Order ID even if the event is not an ecommerce event.', + type: 'string', + required: true, + default: { + '@path': '$.properties.orderId' + } + }, + user_agent: { + label: 'User Agent', + description: 'User Agent of the customer who triggered the conversion event.', + type: 'string', + required: true, + default: { + '@path': '$.context.userAgent' + } + }, + conversion_time: { + label: 'Conversion Time', + description: 'Timestamp of the conversion event.', + type: 'string', + required: true, + default: { + '@path': '$.timestamp' + } + }, + // Conversion Data Fields - These fields are relevant to the conversion, but are not PII, so need not be hashed. + value: { + label: 'Value', + description: 'The monetary value attributed to the conversion event.', + type: 'number', + default: { + '@path': '$.properties.total' + } + }, + currency_code: { + label: 'Currency Code', + description: 'Currency of the purchase or items associated with the event, in 3-letter ISO 4217 format.', + type: 'string', + default: { + '@path': '$.properties.currency' + } + }, + // PII Fields - These fields must be hashed using SHA 256 and encoded as websafe-base64. + phone_number: { + label: 'Phone Number', + description: 'Phone number of the purchaser, in E.164 standard format, e.g. +14150000000', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.phone' }, + then: { '@path': '$.properties.phone' }, + else: { '@path': '$.traits.phone' } + } + } + }, + first_name: { + label: 'First Name', + description: 'First name of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.firstName' }, + then: { '@path': '$.properties.firstName' }, + else: { '@path': '$.traits.firstName' } + } + } + }, + last_name: { + label: 'Last Name', + description: 'Last name of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.lastName' }, + then: { '@path': '$.properties.lastName' }, + else: { '@path': '$.traits.lastName' } + } + } + }, + street_address: { + label: 'Street Address', + description: 'Street address of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.address.street' }, + then: { '@path': '$.properties.address.street' }, + else: { '@path': '$.traits.address.street' } + } + } + }, + city: { + label: 'City', + description: 'City of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.address.city' }, + then: { '@path': '$.properties.address.city' }, + else: { '@path': '$.traits.address.city' } + } + } + }, + region: { + label: 'Region', + description: 'Region of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.address.state' }, + then: { '@path': '$.properties.address.state' }, + else: { '@path': '$.traits.address.state' } + } + } + }, + post_code: { + label: 'Post Code', + description: 'Post code of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.address.postalCode' }, + then: { '@path': '$.properties.address.postalCode' }, + else: { '@path': '$.traits.address.postalCode' } + } + } + }, + country: { + label: 'Country', + description: 'Country of the individual who triggered the conversion event.', + type: 'string', + default: { + '@if': { + exists: { '@path': '$.properties.address.country' }, + then: { '@path': '$.properties.address.country' }, + else: { '@path': '$.traits.address.country' } + } + } + } + }, + + perform: (request, { payload }) => { + const conversionData = cleanData({ + oid: payload.transaction_id, + user_agent: payload.user_agent, + conversion_time: +new Date(payload.conversion_time) * 1000, + label: payload.conversion_label, + value: payload.value, + currency_code: payload.currency_code + }) + + const address = cleanData({ + hashed_first_name: formatFirstName(payload.first_name), + hashed_last_name: formatLastName(payload.last_name), + hashed_street_address: formatStreet(payload.street_address), + city: formatCity(payload.city), + region: formatRegion(payload.region), + postcode: payload.post_code, + country: payload.country + }) + + const pii_data = cleanData({ + hashed_email: formatEmail(payload.email), + hashed_phone_number: [formatPhone(payload.phone_number)] + }) + + return request('https://www.google.com/ads/event/api/v1', { + method: 'post', + json: { + pii_data: { ...pii_data, address: [address] }, + ...conversionData + } + }) + } +} + +/** + * Removes all k:v pairs where the value is falsy. + */ +function cleanData(data: Object) { + return pickBy(data, function (value) { + return value + }) +} + +export default action diff --git a/packages/destination-actions/src/destinations/index.ts b/packages/destination-actions/src/destinations/index.ts new file mode 100644 index 0000000000..a89f25550c --- /dev/null +++ b/packages/destination-actions/src/destinations/index.ts @@ -0,0 +1,73 @@ +import { Destination, DestinationDefinition } from '@segment/actions-core' +import amplitude from './amplitude' +import customerio from './customerio' +import pipedrive from './pipedrive' +import slack from './slack' +import twilio from './twilio' +import googleAnalytics4 from './google-analytics-4' +import googleEnhancedConversions from './google-enhanced-conversions' + +/** + * To use register an integration in the `integrations` service, + * you'll need to add it to the `destinations` export (and corresponding types) + * as well as the `idToSlug` with the corresponding production id. + * + * To test in staging, the ids should match across environments. Typically this is handled by + * creating the destination in production and syncing those definitions to staging with `sprout`. + */ +export type ActionDestinationSlug = + | 'amplitude' + | 'customerio' + | 'pipedrive' + | 'slack' + | 'twilio' + | 'google-analytics-4' + | 'google-enhanced-conversions' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const destinations: Record> = { + amplitude, + customerio, + pipedrive, + slack, + twilio, + 'google-analytics-4': googleAnalytics4, + 'google-enhanced-conversions': googleEnhancedConversions +} + +export const idToSlug: Record = { + '5f7dd6d21ad74f3842b1fc47': 'amplitude', + '5f7dd78fe27ce7ff2b8bfa37': 'customerio', + '5f7dd8191ad74f868ab1fc48': 'pipedrive', + '5f7dd8e302173ff732db5cc4': 'slack', + '602efa1f249b9a5e2bf8a813': 'twilio', + '60ad61f9ff47a16b8fb7b5d9': 'google-analytics-4', + '60ae8b97dcb6cc52d5d0d5ab': 'google-enhanced-conversions' +} + +/** Attempts to load a destination definition from a given file path */ +export async function getDestinationLazy(slug: string): Promise { + const destination = await import(`./${slug}`).then((mod) => mod.default) + + // Loose validation on a destination definition + if (!destination?.name || typeof destination?.actions !== 'object') { + return null + } + + return destination +} + +export async function getDestinationBySlug(slug: string): Promise { + const destination = destinations[slug as ActionDestinationSlug] ?? (await getDestinationLazy(slug)) + + if (!destination) { + throw new Error('Destination not found') + } + + return new Destination(destination) +} + +export async function getDestinationByIdOrSlug(idOrSlug: string): Promise { + const slug = idToSlug[idOrSlug] ?? idOrSlug + return getDestinationBySlug(slug) +} diff --git a/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/generated-types.ts b/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/generated-types.ts new file mode 100644 index 0000000000..f917ad4c17 --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/generated-types.ts @@ -0,0 +1,20 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Identifier used to find existing organization in Pipedrive. Typically this is the name but it can also be a custom field value. Custom organization fields may be included by using the long hash keys of the custom fields. These look like "33595c732cd7a027c458ea115a48a7f8a254fa86". + */ + identifier: string + /** + * Name of the organization + */ + name: string + /** + * ID of the user who will be marked as the owner of this organization. Default is the user who ownes the API token. + */ + owner_id?: number + /** + * If the organization is created, use this timestamp as the creation timestamp. Format: YYY-MM-DD HH:MM:SS + */ + add_time?: string +} diff --git a/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/index.ts b/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/index.ts new file mode 100644 index 0000000000..68d1e8fd9e --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/createUpdateOrganization/index.ts @@ -0,0 +1,77 @@ +import dayjs from '../../../lib/dayjs' +import { get } from '@segment/actions-core' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Create or Update Organization', + description: "Update an organization in Pipedrive or create it if it doesn't exist yet.", + defaultSubscription: 'type = "group"', + fields: { + identifier: { + label: 'Organization ID', + description: + 'Identifier used to find existing organization in Pipedrive. Typically this is the name but it can also be a custom field value. Custom organization fields may be included by using the long hash keys of the custom fields. These look like "33595c732cd7a027c458ea115a48a7f8a254fa86".', + type: 'string', + required: true + }, + name: { + label: 'Organization Name', + description: 'Name of the organization', + type: 'string', + required: true + }, + owner_id: { + label: 'Owner ID', + description: + 'ID of the user who will be marked as the owner of this organization. Default is the user who ownes the API token.', + type: 'number' + }, + add_time: { + label: 'Created At', + description: + 'If the organization is created, use this timestamp as the creation timestamp. Format: YYY-MM-DD HH:MM:SS', + type: 'string' + } + }, + + cachedFields: { + organizationId: { + ttl: 60, + key: ({ payload }) => payload.identifier, + value: async (request, { payload, settings }) => { + const search = await request(`https://${settings.domain}.pipedrive.com/api/v1/organizations/search`, { + searchParams: { term: payload.identifier } + }) + return get(search.data, 'data.items[0].item.id') + } + } + }, + + perform: (request, { payload, settings, cachedFields }) => { + const organizationId = cachedFields.organizationId + + const organization = { + name: payload.name, + owner_id: payload.owner_id + } + + if (organizationId === undefined || organizationId === null) { + return request(`https://${settings.domain}.pipedrive.com/api/v1/organizations`, { + method: 'post', + json: { + ...organization, + add_time: payload.add_time ? dayjs.utc(payload.add_time).format('YYYY-MM-DD HH:MM:SS') : undefined + } + }) + } + + return request(`https://${settings.domain}.pipedrive.com/api/v1/organizations/${organizationId}`, { + method: 'put', + json: organization + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/generated-types.ts b/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/generated-types.ts new file mode 100644 index 0000000000..04e5265c43 --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/generated-types.ts @@ -0,0 +1,28 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Identifier used to find existing person in Pipedrive. Can be an email, name, phone number, or custom field value. Custom person fields may be included by using the long hash keys of the custom fields. These look like "33595c732cd7a027c458ea115a48a7f8a254fa86". + */ + identifier: string + /** + * Name of the person + */ + name: string + /** + * ID of the organization this person will belong to. + */ + org_id?: number + /** + * Email addresses for this person. + */ + email?: string[] + /** + * Phone number for the person. + */ + phone?: string + /** + * If the person is created, use this timestamp as the creation timestamp. Format: YYY-MM-DD HH:MM:SS + */ + add_time?: string +} diff --git a/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/index.ts b/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/index.ts new file mode 100644 index 0000000000..4458de6bf1 --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/createUpdatePerson/index.ts @@ -0,0 +1,144 @@ +import dayjs from '../../../lib/dayjs' +import { get } from '@segment/actions-core' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +interface Organizations { + data: Organization[] + additional_data: { + pagination: { + next_start?: number + } + } +} + +interface Organization { + id: string + name: string +} + +interface Person { + name: string + org_id?: number + email?: string[] + phone?: string + add_time?: string +} + +const action: ActionDefinition = { + title: 'Create or Update Person', + description: "Update a person in Pipedrive or create them if they don't exist yet.", + defaultSubscription: 'type = "identify"', + fields: { + identifier: { + label: 'Person ID', + description: + 'Identifier used to find existing person in Pipedrive. Can be an email, name, phone number, or custom field value. Custom person fields may be included by using the long hash keys of the custom fields. These look like "33595c732cd7a027c458ea115a48a7f8a254fa86".', + type: 'string', + required: true, + default: { + '@path': '$.userId' + } + }, + name: { + label: 'Person Name', + description: 'Name of the person', + type: 'string', + required: true + }, + org_id: { + label: 'Organization ID', + description: 'ID of the organization this person will belong to.', + type: 'number', + dynamic: true + }, + email: { + label: 'Email Address', + description: 'Email addresses for this person.', + type: 'string', + multiple: true + }, + phone: { + label: 'Phone Number', + description: 'Phone number for the person.', + type: 'string' + }, + add_time: { + label: 'Created At', + description: 'If the person is created, use this timestamp as the creation timestamp. Format: YYY-MM-DD HH:MM:SS', + type: 'string' + } + }, + + dynamicFields: { + org_id: async (request, { page, settings }) => { + const searchParams: Record = {} + if (typeof page === 'string') { + searchParams.start = Number(page) + } + + const response = await request(`https://${settings.domain}.pipedrive.com/api/v1/organizations`, { + searchParams + }) + const body = response.data + + const items = body.data.map((organization) => ({ + label: organization.name, + value: organization.id + })) + + let nextPage: string | undefined + + if (typeof body.additional_data.pagination.next_start === 'number') { + nextPage = String(body.additional_data.pagination.next_start) + } + + return { + body: { + data: items, + pagination: { nextPage } + } + } + } + }, + + cachedFields: { + personId: { + ttl: 60, + key: ({ payload }) => payload.identifier, + value: async (request, { payload, settings }) => { + const search = await request(`https://${settings.domain}.pipedrive.com/api/v1/persons/search`, { + searchParams: { term: payload.identifier } + }) + return get(search.data, 'data.items[0].item.id') + } + } + }, + + perform: (request, { payload, settings, cachedFields }) => { + const personId = cachedFields.personId + + const person: Person = { + name: payload.name, + org_id: payload.org_id, + email: payload.email, + phone: payload.phone + } + + if (personId === undefined || personId === null) { + if (payload.add_time) { + person.add_time = dayjs.utc(person.add_time).format('YYYY-MM-DD HH:MM:SS') + } + + return request(`https://${settings.domain}.pipedrive.com/api/v1/persons`, { method: 'post', json: person }) + } + + return request(`https://${settings.domain}.pipedrive.com/api/v1/persons/${personId}`, { + method: 'put', + json: person + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/pipedrive/generated-types.ts b/packages/destination-actions/src/destinations/pipedrive/generated-types.ts new file mode 100644 index 0000000000..7b31fa1bfa --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Pipedrive domain. This is found in Pipedrive in Settings > Company settings > Company domain. + */ + domain: string + /** + * Pipedrive API token. This is found in Pipedrive in Settings > Personal preferences > API > Your personal API token. + */ + apiToken: string +} diff --git a/packages/destination-actions/src/destinations/pipedrive/index.ts b/packages/destination-actions/src/destinations/pipedrive/index.ts new file mode 100644 index 0000000000..07b4769935 --- /dev/null +++ b/packages/destination-actions/src/destinations/pipedrive/index.ts @@ -0,0 +1,46 @@ +import createUpdateOrganization from './createUpdateOrganization' +import createUpdatePerson from './createUpdatePerson' +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' + +const destination: DestinationDefinition = { + name: 'Pipedrive', + authentication: { + scheme: 'custom', + fields: { + domain: { + label: 'Domain', + description: 'Pipedrive domain. This is found in Pipedrive in Settings > Company settings > Company domain.', + type: 'string', + // minLength: 1, + required: true + }, + apiToken: { + label: 'API Token', + description: + 'Pipedrive API token. This is found in Pipedrive in Settings > Personal preferences > API > Your personal API token.', + type: 'string', + // minLength: 20, + required: true + } + }, + testAuthentication: (request, { settings }) => { + return request(`https://${settings.domain}.pipedrive.com/api/v1/users/me`) + } + }, + + extendRequest({ settings }) { + return { + searchParams: { + api_token: settings.apiToken + } + } + }, + + actions: { + createUpdateOrganization, + createUpdatePerson + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/slack/generated-types.ts b/packages/destination-actions/src/destinations/slack/generated-types.ts new file mode 100644 index 0000000000..4ab2786ec6 --- /dev/null +++ b/packages/destination-actions/src/destinations/slack/generated-types.ts @@ -0,0 +1,3 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings {} diff --git a/packages/destination-actions/src/destinations/slack/index.ts b/packages/destination-actions/src/destinations/slack/index.ts new file mode 100644 index 0000000000..2e0c94b220 --- /dev/null +++ b/packages/destination-actions/src/destinations/slack/index.ts @@ -0,0 +1,12 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' +import postToChannel from './postToChannel' + +const destination: DestinationDefinition = { + name: 'Slack', + actions: { + postToChannel + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/slack/postToChannel/generated-types.ts b/packages/destination-actions/src/destinations/slack/postToChannel/generated-types.ts new file mode 100644 index 0000000000..0438c1ab89 --- /dev/null +++ b/packages/destination-actions/src/destinations/slack/postToChannel/generated-types.ts @@ -0,0 +1,24 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * Slack webhook URL. + */ + url: string + /** + * The text message to post to Slack. You can use [Slack's formatting syntax.](https://api.slack.com/reference/surfaces/formatting) + */ + text: string + /** + * Slack channel to post message to. + */ + channel?: string + /** + * User name to post messages as. + */ + username?: string + /** + * URL for user icon image. + */ + icon_url?: string +} diff --git a/packages/destination-actions/src/destinations/slack/postToChannel/index.ts b/packages/destination-actions/src/destinations/slack/postToChannel/index.ts new file mode 100644 index 0000000000..248036211f --- /dev/null +++ b/packages/destination-actions/src/destinations/slack/postToChannel/index.ts @@ -0,0 +1,55 @@ +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Post Message', + description: 'Post a message to a Slack channel.', + fields: { + url: { + label: 'Webhook URL', + description: 'Slack webhook URL.', + type: 'string', + required: true, + format: 'uri' + }, + text: { + label: 'Message', + required: true, + description: + "The text message to post to Slack. You can use [Slack's formatting syntax.](https://api.slack.com/reference/surfaces/formatting)", + type: 'text' + }, + channel: { + label: 'Channel', + description: 'Slack channel to post message to.', + type: 'string' + }, + username: { + label: 'User', + description: 'User name to post messages as.', + type: 'string', + default: 'Segment' + }, + icon_url: { + label: 'Icon URL', + description: 'URL for user icon image.', + type: 'string', + default: 'https://logo.clearbit.com/segment.com' + } + }, + + perform: (request, { payload }) => { + return request(payload.url, { + method: 'post', + json: { + channel: payload.channel, + text: payload.text, + username: payload.username, + icon_url: payload.icon_url + } + }) + } +} + +export default action diff --git a/packages/destination-actions/src/destinations/twilio/__tests__/twilio.test.ts b/packages/destination-actions/src/destinations/twilio/__tests__/twilio.test.ts new file mode 100644 index 0000000000..8ada0c3c0e --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio/__tests__/twilio.test.ts @@ -0,0 +1,78 @@ +import nock from 'nock' +import { createTestEvent, createTestIntegration } from '@segment/actions-core' +import Twilio from '../index' + +const testDestination = createTestIntegration(Twilio) +const timestamp = new Date().toISOString() +const accountId = '_account_' + +describe('Twilio', () => { + describe('sendSMS', () => { + it('should work with default mappings', async () => { + const event = createTestEvent({ + timestamp, + event: 'Test Event', + properties: { + To: '+17758638863', + Body: 'Hello, World!' + } + }) + + nock(`https://api.twilio.com/2010-04-01/Accounts/${accountId}`).post('/Messages.json').reply(201, {}) + + const responses = await testDestination.testAction('sendSMS', { + event, + settings: { + accountId, + phoneNumber: '+12056065576', + token: '_token_' + }, + mapping: { + To: { + '@path': '$.properties.To' + }, + Body: { + '@path': '$.properties.Body' + } + }, + useDefaultMappings: true + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(201) + + expect(responses[0].options.body).toMatchInlineSnapshot(` + URLSearchParams { + Symbol(query): Array [ + "From", + "+12056065576", + "To", + "+17758638863", + "Body", + "Hello, World!", + ], + Symbol(context): null, + } + `) + + expect(responses[0].request.headers).toMatchInlineSnapshot(` + Headers { + Symbol(map): Object { + "Content-Type": Array [ + "application/x-www-form-urlencoded;charset=UTF-8", + ], + "authorization": Array [ + "Basic X2FjY291bnRfOl90b2tlbl8=", + ], + "user-agent": Array [ + "Segment", + ], + }, + } + `) + + expect(responses[0].options.username).toMatchInlineSnapshot(`"_account_"`) + expect(responses[0].options.password).toMatchInlineSnapshot(`"_token_"`) + }) + }) +}) diff --git a/packages/destination-actions/src/destinations/twilio/generated-types.ts b/packages/destination-actions/src/destinations/twilio/generated-types.ts new file mode 100644 index 0000000000..dcabc8c560 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio/generated-types.ts @@ -0,0 +1,16 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Settings { + /** + * Your Twilio Account Id + */ + accountId: string + /** + * Your Twilio Token. + */ + token: string + /** + * Your Twilio Phone Number with Country Code. + */ + phoneNumber: string +} diff --git a/packages/destination-actions/src/destinations/twilio/index.ts b/packages/destination-actions/src/destinations/twilio/index.ts new file mode 100644 index 0000000000..74420cb397 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio/index.ts @@ -0,0 +1,44 @@ +import type { DestinationDefinition } from '@segment/actions-core' +import type { Settings } from './generated-types' +import sendSMS from './sendSms' + +const destination: DestinationDefinition = { + name: 'Twilio', + authentication: { + scheme: 'basic', + fields: { + accountId: { + label: 'Account Id', + description: 'Your Twilio Account Id', + type: 'string', + required: true + }, + token: { + label: 'Token', + description: 'Your Twilio Token.', + type: 'string', + required: true + }, + phoneNumber: { + label: 'Phone Number', + description: 'Your Twilio Phone Number with Country Code.', + type: 'string', + required: true + } + }, + testAuthentication: (request) => { + return request('https://api.twilio.com/2010-04-01/Accounts') + } + }, + extendRequest({ settings }) { + return { + username: settings.accountId, + password: settings.token + } + }, + actions: { + sendSMS + } +} + +export default destination diff --git a/packages/destination-actions/src/destinations/twilio/sendSms/generated-types.ts b/packages/destination-actions/src/destinations/twilio/sendSms/generated-types.ts new file mode 100644 index 0000000000..6eafcf1707 --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio/sendSms/generated-types.ts @@ -0,0 +1,12 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The Phone Number to send a SMS to + */ + To: string + /** + * The message body + */ + Body: string +} diff --git a/packages/destination-actions/src/destinations/twilio/sendSms/index.ts b/packages/destination-actions/src/destinations/twilio/sendSms/index.ts new file mode 100644 index 0000000000..543d65048d --- /dev/null +++ b/packages/destination-actions/src/destinations/twilio/sendSms/index.ts @@ -0,0 +1,37 @@ +import { URLSearchParams } from 'url' +import type { ActionDefinition } from '@segment/actions-core' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' + +const action: ActionDefinition = { + title: 'Send SMS', + description: 'Sends an SMS message', + defaultSubscription: 'type = "track"', + fields: { + To: { + label: 'To', + description: 'The Phone Number to send a SMS to', + type: 'string', + required: true + }, + Body: { + label: 'Body', + description: 'The message body', + type: 'text', + required: true + } + }, + perform: (request, data) => { + return request(`https://api.twilio.com/2010-04-01/Accounts/${data.settings.accountId}/Messages.json`, { + method: 'post', + // Fetch will automatically set the content-type for this `body` + // to application/x-www-form-urlencoded;charset=UTF-8 + body: new URLSearchParams({ + From: data.settings.phoneNumber, + ...data.payload + }) + }) + } +} + +export default action diff --git a/packages/destination-actions/src/index.ts b/packages/destination-actions/src/index.ts new file mode 100644 index 0000000000..b08e9bee22 --- /dev/null +++ b/packages/destination-actions/src/index.ts @@ -0,0 +1,17 @@ +export * from './destinations' + +export { Destination } from '@segment/actions-core' + +export type { + ActionDefinition, + DestinationDefinition, + SubscriptionStats, + AuthenticationScheme, + BasicAuthentication, + CustomAuthentication, + OAuth2Authentication, + OAuth2ClientCredentials, + RefreshAccessTokenResult +} from '@segment/actions-core' + +export type { SegmentEvent } from '@segment/actions-core' diff --git a/packages/destination-actions/src/lib/dayjs.ts b/packages/destination-actions/src/lib/dayjs.ts new file mode 100644 index 0000000000..7d52a21a0c --- /dev/null +++ b/packages/destination-actions/src/lib/dayjs.ts @@ -0,0 +1,6 @@ +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' + +dayjs.extend(utc) + +export default dayjs diff --git a/packages/destination-actions/test/setup-after-env.ts b/packages/destination-actions/test/setup-after-env.ts new file mode 100644 index 0000000000..93b3c4373f --- /dev/null +++ b/packages/destination-actions/test/setup-after-env.ts @@ -0,0 +1,17 @@ +import nock from 'nock' + +beforeEach(() => { + // Removes mocks between unit tests so they run in isolation + nock.cleanAll() +}) + +beforeAll(() => { + if (!nock.isActive()) { + nock.activate() + } +}) + +afterAll(() => { + // Avoids memory-leaks with the way nock monkey-patches http and Jest messes with modules + nock.restore() +}) diff --git a/packages/destination-actions/tsconfig.build.json b/packages/destination-actions/tsconfig.build.json new file mode 100644 index 0000000000..982a7807e2 --- /dev/null +++ b/packages/destination-actions/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "exclude": ["**/__tests__/**/*.ts"], + "include": ["src"] +} diff --git a/packages/destination-actions/tsconfig.json b/packages/destination-actions/tsconfig.json new file mode 100644 index 0000000000..f9b39acaae --- /dev/null +++ b/packages/destination-actions/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@segment/actions-core": ["../core/src"] + } + }, + "exclude": [], + "include": ["src", "test"] +} diff --git a/packages/destination-subscriptions/package.json b/packages/destination-subscriptions/package.json new file mode 100644 index 0000000000..c3abb05204 --- /dev/null +++ b/packages/destination-subscriptions/package.json @@ -0,0 +1,75 @@ +{ + "name": "@segment/destination-subscriptions", + "version": "3.0.0", + "description": "Validate event payload using subscription AST", + "repository": { + "type": "git", + "url": "https://github.com/segmentio/action-destinations", + "directory": "packages/destination-subscriptions" + }, + "scripts": { + "build": "yarn clean && yarn build:cjs && yarn build:esm", + "build:cjs": "yarn tsc -p tsconfig.build.json -m commonjs --outDir dist/cjs", + "build:esm": "yarn tsc -p tsconfig.build.json -m es2015 --outDir dist/esm", + "clean": "tsc -b tsconfig.build.json --clean", + "postclean": "rm -rf dist", + "test": "jest", + "typecheck": "tsc --noEmit", + "prepare": "yarn build", + "size": "size-limit" + }, + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "exports": { + ".": { + "require": "./dist/cjs/index.js", + "default": "./dist/esm/index.js" + }, + "./": { + "require": "./dist/cjs/", + "default": "./dist/esm/" + } + }, + "sideEffects": false, + "files": [ + "dist", + "package.json" + ], + "dependencies": { + "@segment/fql": "^1.9.1" + }, + "devDependencies": { + "@sindresorhus/tsconfig": "^0.7.0", + "@size-limit/preset-small-lib": "^4.10.1", + "@types/jest": "^26.0.15", + "eslint-config-xo-typescript": "^0.35.0", + "eslint-plugin-prettier": "^3.1.3", + "jest": "^26.0.1", + "prettier": "^2.0.5", + "size-limit": "^4.10.1", + "typescript": "^4.0.5" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "modulePathIgnorePatterns": [ + "/dist/" + ] + }, + "publishConfig": { + "access": "restricted", + "registry": "https://registry.npmjs.org" + }, + "size-limit": [ + { + "path": "dist/esm/index.js", + "import": "{ parseFql, validate }", + "limit": "29 KB" + }, + { + "path": "dist/cjs/index.js", + "limit": "29 KB" + } + ] +} diff --git a/packages/destination-subscriptions/src/__tests__/fql.test.ts b/packages/destination-subscriptions/src/__tests__/fql.test.ts new file mode 100644 index 0000000000..2b3a0b45f8 --- /dev/null +++ b/packages/destination-subscriptions/src/__tests__/fql.test.ts @@ -0,0 +1,497 @@ +import { parseFql, generateFql } from '../index' + +const testFql = (fql: string, ast: any): void => { + expect(parseFql(fql)).toEqual(ast) + expect(generateFql(ast)).toEqual(fql) +} + +const expectedTypeError = new Error("Cannot read property 'type' of undefined") +expectedTypeError.name = 'TypeError' + +test('should handle invalid payloads', () => { + expect(parseFql('typo')).toEqual({ + error: expectedTypeError + }) +}) + +test('should handle incomplete payloads', () => { + const expectedError = new Error('Value token is missing') + expect(parseFql('type')).toEqual({ + error: expectedError + }) +}) + +test('should handle invalid operators', () => { + expect(parseFql('type * "32456"')).toEqual({ + error: expectedTypeError + }) +}) + +test('should handle empty payload', () => { + expect(parseFql('')).toEqual({ + error: expectedTypeError + }) +}) + +test('should handle missing values', () => { + expect(parseFql('type = ')).toEqual({ + children: [ + { + operator: '=', + type: 'event-type', + value: 'eos' + } + ], + operator: 'and', + type: 'group' + }) +}) + +test('type = "track"', () => { + testFql('type = "track"', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + } + ] + }) +}) + +test('type = "track" or type = "identify"', () => { + testFql('type = "track" or type = "identify"', { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'event-type', + operator: '=', + value: 'identify' + } + ] + }) +}) + +test('event = "Product Added"', () => { + testFql('event = "Product Added"', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + } + ] + }) +}) + +test('event = "Product Added" or event = "Order Completed"', () => { + testFql('event = "Product Added" or event = "Order Completed"', { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event', + operator: '=', + value: 'Order Completed' + } + ] + }) +}) + +test('type = "track" and event = "Product Added"', () => { + testFql('type = "track" and event = "Product Added"', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'event', + operator: '=', + value: 'Product Added' + } + ] + }) +}) + +test('type = "track" and (event = "Product Added" or event = "Order Completed")', () => { + testFql('type = "track" and (event = "Product Added" or event = "Order Completed")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event', + operator: '=', + value: 'Order Completed' + } + ] + } + ] + }) +}) + +test('event = "Product Added" and property "price" >= 100', () => { + testFql('event = "Product Added" and properties.price >= 100', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'price', + operator: '>=', + value: 100 + } + ] + }) +}) + +test('event = "Product Added" and property "price" >= 100 and property "premium" = true', () => { + testFql('event = "Product Added" and properties.price >= 100 and properties.premium = "true"', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'price', + operator: '>=', + value: 100 + }, + { + type: 'event-property', + name: 'premium', + operator: '=', + value: 'true' + } + ] + }) +}) + +test('(event = "Product Added" and property "price" >= 100) or (event = "Order Completed" and property "total" >= 500)', () => { + testFql( + '(event = "Product Added" and properties.price >= 100) or (event = "Order Completed" and properties.total >= 500)', + { + type: 'group', + operator: 'or', + children: [ + { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + operator: '>=', + name: 'price', + value: 100 + } + ] + }, + { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Order Completed' + }, + { + type: 'event-property', + name: 'total', + operator: '>=', + value: 500 + } + ] + } + ] + } + ) +}) + +test('support contains for event name', () => { + testFql('contains(event, "Nike")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'contains', + value: 'Nike' + } + ] + }) +}) + +test('support contains for event property', () => { + testFql('event = "Product Added" and contains(properties.name, "Nike")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'contains', + value: 'Nike' + } + ] + }) +}) + +test('support not_contains for event name', () => { + testFql('!contains(event, "Nike")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'not_contains', + value: 'Nike' + } + ] + }) +}) + +test('support not_contains for event property', () => { + testFql('event = "Product Added" and !contains(properties.name, "Nike")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'not_contains', + value: 'Nike' + } + ] + }) +}) + +test('support starts_with for event name', () => { + testFql('match(event, "X*")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'starts_with', + value: 'X' + } + ] + }) +}) + +test('support starts_with for event property', () => { + testFql('event = "Product Added" and match(properties.name, "X*")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'starts_with', + value: 'X' + } + ] + }) +}) + +test('support not_starts_with for event name', () => { + testFql('!match(event, "X*")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'not_starts_with', + value: 'X' + } + ] + }) +}) + +test('support not_starts_with for event property', () => { + testFql('event = "Product Added" and !match(properties.name, "X*")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'not_starts_with', + value: 'X' + } + ] + }) +}) + +test('support ends_with for event name', () => { + testFql('match(event, "*X")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'ends_with', + value: 'X' + } + ] + }) +}) + +test('support ends_with for event property', () => { + testFql('event = "Product Added" and match(properties.name, "*X")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'ends_with', + value: 'X' + } + ] + }) +}) + +test('support not_ends_with for event name', () => { + testFql('!match(event, "*X")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: 'not_ends_with', + value: 'X' + } + ] + }) +}) + +test('support not_ends_with for event property', () => { + testFql('event = "Product Added" and !match(properties.name, "*X")', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'not_ends_with', + value: 'X' + } + ] + }) +}) + +test('support exists for event property', () => { + testFql('event = "Product Added" and properties.name != null', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'exists' + } + ] + }) +}) + +test('support not_exists for event property', () => { + testFql('event = "Product Added" and properties.name = null', { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Product Added' + }, + { + type: 'event-property', + name: 'name', + operator: 'not_exists' + } + ] + }) +}) diff --git a/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts b/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts new file mode 100644 index 0000000000..e8bf24df52 --- /dev/null +++ b/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts @@ -0,0 +1,105 @@ +import { generateFql, Subscription } from '../index' + +test('should handle error in ast', () => { + const ast = { + error: new Error("Cannot read property 'type' of undefined") + } + try { + generateFql(ast) + fail('should have thrown an error') + } catch (error: unknown) { + expect(error).toEqual(new Error("Cannot read property 'type' of undefined")) + } +}) + +test('should handle valid ast', () => { + const ast: Subscription = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value: 'x' + } + ] + } + + expect(generateFql(ast)).toEqual('properties.value = "x"') +}) + +test('should handle ast with multiple childs (or condition)', () => { + const ast: Subscription = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'event-type', + operator: '=', + value: 'identify' + } + ] + } + + expect(generateFql(ast)).toEqual('type = "track" or type = "identify"') +}) + +test('should handle ast with multiple childs (and condition)', () => { + const ast: Subscription = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event-property', + name: 'name', + operator: '=', + value: 'Catalog' + } + ] + } + + expect(generateFql(ast)).toEqual('event = "Page Viewed" and properties.name = "Catalog"') +}) + +test('should handle ast with nested childs', () => { + const ast: Subscription = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event', + operator: '=', + value: 'Order Completed' + } + ] + } + ] + } + + expect(generateFql(ast)).toEqual('type = "track" and (event = "Page Viewed" or event = "Order Completed")') +}) diff --git a/packages/destination-subscriptions/src/__tests__/validate.test.ts b/packages/destination-subscriptions/src/__tests__/validate.test.ts new file mode 100644 index 0000000000..31cbf57ce0 --- /dev/null +++ b/packages/destination-subscriptions/src/__tests__/validate.test.ts @@ -0,0 +1,702 @@ +import { validate as originalValidate } from '../index' + +// Helper to avoid caring about types in tests +const validate = (ast: any, data: any): boolean => originalValidate(ast, data) + +test('should handle error in ast', () => { + const ast = { + error: new Error("Cannot read property 'type' of undefined") + } + + expect(validate(ast, { properties: { value: 'x' } })).toEqual(false) +}) + +test('should handle undefined event', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value: 'x' + } + ] + } + + expect(validate(ast, undefined)).toEqual(false) +}) + +test('should handle empty string as event', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value: 'x' + } + ] + } + + expect(validate(ast, '')).toEqual(false) +}) + +test('should handle empty event', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value: 'x' + } + ] + } + + expect(validate(ast, {})).toEqual(false) +}) + +test('operators - equals (strings)', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'x' } })).toEqual(true) + expect(validate(ast, { properties: { value: 'y' } })).toEqual(false) +}) + +test('operators - equals (numbers)', () => { + for (const value of ['123', 123]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '=', + value + } + ] + } + + expect(validate(ast, { properties: { value: 123 } })).toEqual(true) + expect(validate(ast, { properties: { value: '123' } })).toEqual(true) + expect(validate(ast, { properties: { value: 0 } })).toEqual(false) + } +}) + +test('operators - not equals (strings)', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '!=', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'x' } })).toEqual(false) + expect(validate(ast, { properties: { value: 'y' } })).toEqual(true) +}) + +test('operators - not equals (numbers)', () => { + for (const value of ['123', 123]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '!=', + value + } + ] + } + + expect(validate(ast, { properties: { value: 123 } })).toEqual(false) + expect(validate(ast, { properties: { value: '123' } })).toEqual(false) + expect(validate(ast, { properties: { value: 456 } })).toEqual(true) + expect(validate(ast, { properties: { value: '456' } })).toEqual(true) + } +}) + +test('operators - less than', () => { + for (const value of ['10', 10]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '<', + value + } + ] + } + + expect(validate(ast, { properties: { value: 5 } })).toEqual(true) + expect(validate(ast, { properties: { value: '5' } })).toEqual(true) + expect(validate(ast, { properties: { value: 10 } })).toEqual(false) + expect(validate(ast, { properties: { value: '10' } })).toEqual(false) + } +}) + +test('operators - less than or equal', () => { + for (const value of ['10', 10]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '<=', + value + } + ] + } + + expect(validate(ast, { properties: { value: 5 } })).toEqual(true) + expect(validate(ast, { properties: { value: '5' } })).toEqual(true) + expect(validate(ast, { properties: { value: 10 } })).toEqual(true) + expect(validate(ast, { properties: { value: '10' } })).toEqual(true) + expect(validate(ast, { properties: { value: 11 } })).toEqual(false) + expect(validate(ast, { properties: { value: '11' } })).toEqual(false) + } +}) + +test('operators - greater than', () => { + for (const value of ['10', 10]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '>', + value + } + ] + } + + expect(validate(ast, { properties: { value: 11 } })).toEqual(true) + expect(validate(ast, { properties: { value: '11' } })).toEqual(true) + expect(validate(ast, { properties: { value: 10 } })).toEqual(false) + expect(validate(ast, { properties: { value: '10' } })).toEqual(false) + expect(validate(ast, { properties: { value: 5 } })).toEqual(false) + expect(validate(ast, { properties: { value: '5' } })).toEqual(false) + } +}) + +test('operators - greater than or equal', () => { + for (const value of ['10', 10]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: '>=', + value + } + ] + } + + expect(validate(ast, { properties: { value: 11 } })).toEqual(true) + expect(validate(ast, { properties: { value: '11' } })).toEqual(true) + expect(validate(ast, { properties: { value: 10 } })).toEqual(true) + expect(validate(ast, { properties: { value: '10' } })).toEqual(true) + expect(validate(ast, { properties: { value: 5 } })).toEqual(false) + expect(validate(ast, { properties: { value: '5' } })).toEqual(false) + } +}) + +test('operators - contains', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'contains', + value: 'b' + } + ] + } + + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(true) + expect(validate(ast, { properties: { value: 'xyz' } })).toEqual(false) +}) + +test('operators - not contains', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'not_contains', + value: 'b' + } + ] + } + + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(false) + expect(validate(ast, { properties: { value: 'xyz' } })).toEqual(true) +}) + +test('operators - starts with', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'starts_with', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'xabc' } })).toEqual(true) + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(false) +}) + +test('operators - not starts with', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'not_starts_with', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'xabc' } })).toEqual(false) + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(true) +}) + +test('operators - ends with', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'ends_with', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'abcx' } })).toEqual(true) + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(false) +}) + +test('operators - not ends with', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'not_ends_with', + value: 'x' + } + ] + } + + expect(validate(ast, { properties: { value: 'abcx' } })).toEqual(false) + expect(validate(ast, { properties: { value: 'abc' } })).toEqual(true) +}) + +test('operators - exists', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'exists' + } + ] + } + + expect(validate(ast, { properties: { value: 'abcx' } })).toEqual(true) + expect(validate(ast, { properties: {} })).toEqual(false) + expect(validate(ast, { properties: { value: null } })).toEqual(false) +}) + +test('operators - not exists', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'not_exists' + } + ] + } + + expect(validate(ast, { properties: { value: 'abcx' } })).toEqual(false) + expect(validate(ast, { properties: {} })).toEqual(true) + expect(validate(ast, { properties: { value: null } })).toEqual(true) +}) + +test('event type = "track"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + } + ] + } + + expect(validate(ast, { type: 'track' })).toEqual(true) + expect(validate(ast, { type: 'identify' })).toEqual(false) +}) + +test('event type = "track" or "identify"', () => { + const ast = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'event-type', + operator: '=', + value: 'identify' + } + ] + } + + expect(validate(ast, { type: 'track' })).toEqual(true) + expect(validate(ast, { type: 'identify' })).toEqual(true) + expect(validate(ast, { type: 'group' })).toEqual(false) +}) + +test('event type = not "group"', () => { + const ast = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-type', + operator: '!=', + value: 'group' + } + ] + } + + expect(validate(ast, { type: 'track' })).toEqual(true) + expect(validate(ast, { type: 'identify' })).toEqual(true) + expect(validate(ast, { type: 'group' })).toEqual(false) +}) + +test('event = "Page Viewed"', () => { + const ast = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed' })).toEqual(true) + expect(validate(ast, { event: 'Product Added' })).toEqual(false) +}) + +test('event = "Page Viewed" or "Product Viewed"', () => { + const ast = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event', + operator: '=', + value: 'Product Viewed' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed' })).toEqual(true) + expect(validate(ast, { event: 'Product Viewed' })).toEqual(true) + expect(validate(ast, { event: 'Product Added' })).toEqual(false) +}) + +test('event = "Page Viewed" or event type = "track"', () => { + const ast = { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event-type', + operator: '=', + value: 'track' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed' })).toEqual(true) + expect(validate(ast, { type: 'track' })).toEqual(true) + expect(validate(ast, { event: 'Product Added' })).toEqual(false) +}) + +test('event = "Page Viewed" and event type = "track"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event-type', + operator: '=', + value: 'track' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed' })).toEqual(false) + expect(validate(ast, { type: 'track' })).toEqual(false) + expect(validate(ast, { event: 'Page Viewed', type: 'track' })).toEqual(true) +}) + +test('event = "Page Viewed" and event property "name" = "Catalog"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event-property', + name: 'name', + operator: '=', + value: 'Catalog' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed', properties: { name: 'Catalog' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed' })).toEqual(false) + expect(validate(ast, { event: 'Page Viewed', properties: { name: 'Other' } })).toEqual(false) +}) + +test('event = "Page Viewed" and event property "name" = "Catalog" or "Home"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-property', + name: 'name', + operator: '=', + value: 'Catalog' + }, + { + type: 'event-property', + name: 'name', + operator: '=', + value: 'Home' + } + ] + } + ] + } + + expect(validate(ast, { event: 'Page Viewed', properties: { name: 'Catalog' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed', properties: { name: 'Home' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed', properties: { name: 'Other' } })).toEqual(false) +}) + +test('event = "Page Viewed" and event context "ip" = "1.1.1.1"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event-context', + name: 'ip', + operator: '=', + value: '1.1.1.1' + } + ] + } + + expect(validate(ast, { event: 'Page Viewed', context: { ip: '1.1.1.1' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed' })).toEqual(false) + expect(validate(ast, { event: 'Page Viewed', context: { ip: '8.8.8.8' } })).toEqual(false) +}) + +test('event = "Page Viewed" and event context "ip" = "1.1.1.1" or "2.2.2.2"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'group', + operator: 'or', + children: [ + { + type: 'event-context', + name: 'ip', + operator: '=', + value: '1.1.1.1' + }, + { + type: 'event-property', + name: 'ip', + operator: '=', + value: '2.2.2.2' + } + ] + } + ] + } + + expect(validate(ast, { event: 'Page Viewed', context: { ip: '1.1.1.1' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed', context: { ip: '2.2.2.2' } })).toEqual(false) + expect(validate(ast, { event: 'Page Viewed' })).toEqual(false) + expect(validate(ast, { event: 'Page Viewed', context: { ip: '8.8.8.8' } })).toEqual(false) +}) + +test('event type = "track" and event = "Page Viewed" or event = "Order Completed"', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-type', + operator: '=', + value: 'track' + }, + { + type: 'group', + operator: 'or', + children: [ + { + type: 'event', + operator: '=', + value: 'Page Viewed' + }, + { + type: 'event', + operator: '=', + value: 'Order Completed' + } + ] + } + ] + } + + expect( + validate(ast, { + type: 'track', + event: 'Page Viewed' + }) + ).toEqual(true) +}) + +test('pull properties from `traits` object for "identify" and "group" events', () => { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'name', + operator: '=', + value: 'Jane Hopper' + } + ] + } + + expect(validate(ast, { type: 'identify', traits: { name: 'Jane Hopper' } })).toEqual(true) + expect(validate(ast, { type: 'group', traits: { name: 'Jane Hopper' } })).toEqual(true) + expect(validate(ast, { event: 'Page Viewed' })).toEqual(false) + expect(validate(ast, { type: 'identify', traits: { name: 'Demogorgon' } })).toEqual(false) +}) diff --git a/packages/destination-subscriptions/src/generate-fql.ts b/packages/destination-subscriptions/src/generate-fql.ts new file mode 100644 index 0000000000..2c660d972b --- /dev/null +++ b/packages/destination-subscriptions/src/generate-fql.ts @@ -0,0 +1,119 @@ +import { + Subscription, + GroupCondition, + EventTypeCondition, + EventCondition, + EventPropertyCondition, + EventContextCondition, + Operator, + ErrorCondition +} from './types' + +const stringifyValue = (value: string | boolean | number | undefined): string => { + if (typeof value === 'boolean' || typeof value === 'number') { + return String(value) + } + + return `"${value}"` +} + +const fqlExpression = (name: string, operator: Operator, value: string | boolean | number | undefined): string => { + switch (operator) { + case 'contains': + return `contains(${name}, ${stringifyValue(value)})` + case 'not_contains': + return `!contains(${name}, ${stringifyValue(value)})` + case 'exists': + return `${name} != null` + case 'not_exists': + return `${name} = null` + case 'starts_with': + return `match(${name}, "${String(value)}*")` + case 'not_starts_with': + return `!match(${name}, "${String(value)}*")` + case 'ends_with': + return `match(${name}, "*${String(value)}")` + case 'not_ends_with': + return `!match(${name}, "*${String(value)}")` + case '<': + case '>': + case '<=': + case '>=': + return `${name} ${operator} ${Number(value)}` + default: + return `${name} ${operator} ${stringifyValue(value)}` + } +} + +const stringifyGroupNode = (node: GroupCondition): string => { + return node.children + .map((childNode) => { + if (childNode.type === 'group') { + return `(${stringifyGroupNode(childNode)})` + } + + return stringifyChildNode(childNode) + }) + .join(` ${node.operator} `) +} + +const stringifyChildNode = ( + node: EventTypeCondition | EventCondition | EventPropertyCondition | EventContextCondition +): string => { + let result = '' + + switch (node.type) { + case 'event': { + result += fqlExpression('event', node.operator, node.value) + break + } + + case 'event-type': { + result += fqlExpression('type', node.operator, node.value) + break + } + + case 'event-property': { + result += fqlExpression(`properties.${node.name}`, node.operator, node.value) + break + } + + case 'event-context': { + result += fqlExpression(`context.${node.name}`, node.operator, node.value) + break + } + + default: + throw new Error('Unknown condition type') + } + + return result +} + +const numberOfParens = (string: string): number => { + let parens = 0 + + for (const char of string.split('')) { + if (char === '(' || char === ')') { + parens++ + } + } + + return parens +} + +const generateFql = (ast: Subscription): string => { + if ((ast as ErrorCondition).error) { + throw (ast as ErrorCondition).error + } + + const fql = stringifyGroupNode(ast as GroupCondition) + + if (fql.startsWith('(') && fql.endsWith(')') && numberOfParens(fql) === 2) { + return fql.slice(1, -1) + } + + return fql +} + +export default generateFql diff --git a/packages/destination-subscriptions/src/get.ts b/packages/destination-subscriptions/src/get.ts new file mode 100644 index 0000000000..29d98fac92 --- /dev/null +++ b/packages/destination-subscriptions/src/get.ts @@ -0,0 +1,25 @@ +/** + * Lightweight alternative to lodash.get with similar coverage + * Supports basic path lookup via dot notation `'foo.bar[0].baz'` or an array ['foo', 'bar', '0', 'baz'] + */ +export function get( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + object: any, + path: string | string[], + defValue?: Default +): T | undefined | Default { + // If path is not defined or it has false value + if (!path) return defValue + + // Check if path is string or array. Regex : ensure that we do not have '.' and brackets. + // Regex explained: https://regexr.com/58j0k + const pathArray = Array.isArray(path) ? path : (path.match(/([^[.\]])+/g) as string[]) + + // Find value if exist return otherwise return undefined value + const value = pathArray.reduce( + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain + (previousObject, key) => previousObject && previousObject[key], + object + ) + return typeof value === 'undefined' ? defValue : value +} diff --git a/packages/destination-subscriptions/src/index.ts b/packages/destination-subscriptions/src/index.ts new file mode 100644 index 0000000000..a94c48871c --- /dev/null +++ b/packages/destination-subscriptions/src/index.ts @@ -0,0 +1,4 @@ +export * from './types' +export { default as validate } from './validate' +export { default as generateFql } from './generate-fql' +export { default as parseFql } from './parse-fql' diff --git a/packages/destination-subscriptions/src/parse-fql.ts b/packages/destination-subscriptions/src/parse-fql.ts new file mode 100644 index 0000000000..2b493abe04 --- /dev/null +++ b/packages/destination-subscriptions/src/parse-fql.ts @@ -0,0 +1,274 @@ +import { lex, Token, types as TokenType } from '@segment/fql' +import { Subscription, Condition, GroupConditionOperator, Operator, ConditionType } from './types' + +const tokenToConditionType: Record = { + type: 'event-type', + event: 'event', + properties: 'event-property', + traits: 'event-property' +} + +const getTokenValue = (token: Token): string | number | boolean => { + if (token.type === 'string') { + return token.value.replace(/^"/, '').replace(/"$/, '') + } + + if (token.type === 'number') { + return Number(token.value) + } + + if (token.type === 'ident' && ['true', 'false'].includes(token.value)) { + return token.value === 'true' + } + + return String(token.value) +} + +const isFqlFunction = (token: Token): boolean => { + return token.type === 'ident' && ['contains', 'match'].includes(token.value) +} + +const parseFqlFunction = ( + name: string, + nodes: Condition[], + tokens: Token[], + { negate }: { negate: boolean } = { negate: false } +): void => { + if (name === 'contains') { + // Skip "(" token + tokens.shift() + + const nameToken = tokens.shift() + + if (!nameToken) { + throw new Error('contains() is missing a 1st argument') + } + + // Skip "," token + tokens.shift() + + const valueToken = tokens.shift() + + if (!valueToken) { + throw new Error('contains() is missing a 2nd argument') + } + + // Skip ")" token + tokens.shift() + + if (nameToken.value === 'event') { + nodes.push({ + type: 'event', + operator: negate ? 'not_contains' : 'contains', + value: String(getTokenValue(valueToken)) + }) + } + + if (/^(properties|traits)/.test(nameToken.value)) { + nodes.push({ + type: 'event-property', + name: nameToken.value.replace(/^(properties|traits)\./, ''), + operator: negate ? 'not_contains' : 'contains', + value: String(getTokenValue(valueToken)) + }) + } + } + + if (name === 'match') { + // Skip "(" token + tokens.shift() + + const nameToken = tokens.shift() + + if (!nameToken) { + throw new Error('match() is missing a 1st argument') + } + + // Skip "," token + tokens.shift() + + const valueToken = tokens.shift() + + if (!valueToken) { + throw new Error('match() is missing a 2nd argument') + } + + // Skip ")" token + tokens.shift() + + let operator: Operator | undefined + let value: string | undefined + + if (valueToken.value.endsWith('*"')) { + operator = negate ? 'not_starts_with' : 'starts_with' + value = String(getTokenValue(valueToken)).slice(0, -1) + } else { + operator = negate ? 'not_ends_with' : 'ends_with' + value = String(getTokenValue(valueToken)).slice(1) + } + + if (nameToken.value === 'event') { + nodes.push({ + type: 'event', + operator, + value + }) + } + + if (/^(properties|traits)/.test(nameToken.value)) { + nodes.push({ + type: 'event-property', + name: nameToken.value.replace(/^(properties|traits)\./, ''), + operator, + value + }) + } + } +} + +const parse = (tokens: Token[]): Condition => { + const nodes: Condition[] = [] + let operator = 'and' + let token = tokens.shift() + + while (token && token.type !== 'eos') { + if (token.type === 'ident') { + const [tokenStart] = (token.value ?? '').split('.') + const conditionType = tokenToConditionType[tokenStart] + + if (conditionType) { + const operatorToken = tokens.shift() + + if (!operatorToken) { + throw new Error('Operator token is missing') + } + + const valueToken = tokens.shift() + + if (!valueToken) { + throw new Error('Value token is missing') + } + + if (conditionType === 'event') { + nodes.push({ + type: 'event', + operator: operatorToken.value as Operator, + value: String(getTokenValue(valueToken)) + }) + } else if (conditionType === 'event-type') { + nodes.push({ + type: 'event-type', + operator: operatorToken.value as Operator, + value: String(getTokenValue(valueToken)) + }) + } else if (conditionType === 'event-property') { + if (operatorToken.value === '!=' && valueToken.value === 'null') { + nodes.push({ + type: 'event-property', + name: token.value.replace(/^(properties|traits)\./, ''), + operator: 'exists' + }) + } else if (operatorToken.value === '=' && valueToken.value === 'null') { + nodes.push({ + type: 'event-property', + name: token.value.replace(/^(properties|traits)\./, ''), + operator: 'not_exists' + }) + } else { + nodes.push({ + type: 'event-property', + name: token.value.replace(/^(properties|traits)\./, ''), + operator: operatorToken.value as Operator, + value: getTokenValue(valueToken) + }) + } + } + } + + if (isFqlFunction(token)) { + parseFqlFunction(token.value, nodes, tokens) + } + } + + if (token.type === 'operator' && token.value === '!') { + if (isFqlFunction(tokens[0])) { + const name = tokens[0].value + tokens.shift() + parseFqlFunction(name, nodes, tokens, { negate: true }) + } + } + + if (token.type === 'parenleft') { + const groupTokens: Token[] = [] + let groupToken = tokens.shift() + + while (groupToken?.type !== 'parenright') { + groupTokens.push(groupToken as Token) + groupToken = tokens.shift() + } + + groupTokens.push({ type: TokenType.EOS, value: 'eos' }) + nodes.push(parse(groupTokens)) + } + + if (token.type === 'conditional') { + operator = token.value + } + + token = tokens.shift() + } + + if (nodes.length > 1) { + return { + type: 'group', + operator: operator as GroupConditionOperator, + children: nodes + } + } + + return nodes[0] +} + +const normalize = (tokens: Token[]): Token[] => { + const normalizedTokens: Token[] = [] + let index = 0 + + while (tokens[index]) { + if (tokens[index].type === 'ident' && tokens[index + 1].type === 'dot' && tokens[index + 2].type === 'ident') { + normalizedTokens.push({ + type: TokenType.Ident, + value: tokens[index].value + tokens[index + 1].value + tokens[index + 2].value + }) + + index += 3 + } else { + normalizedTokens.push(tokens[index]) + index++ + } + } + + return normalizedTokens +} + +const parseFql = (fql: string): Subscription => { + try { + const ast = parse(normalize(lex(fql).tokens)) + + if (ast.type !== 'group') { + return { + type: 'group', + operator: 'and', + children: [ast] + } + } + + return ast + } catch (error: unknown) { + const err = error instanceof Error ? error : new Error(`Error while parsing ${fql}`) + return { + error: err + } + } +} + +export default parseFql diff --git a/packages/destination-subscriptions/src/types.ts b/packages/destination-subscriptions/src/types.ts new file mode 100644 index 0000000000..1f5c2644a1 --- /dev/null +++ b/packages/destination-subscriptions/src/types.ts @@ -0,0 +1,65 @@ +export type Subscription = ErrorCondition | GroupCondition +export interface GroupCondition { + type: 'group' + operator: GroupConditionOperator + children: T[] +} + +export interface ErrorCondition { + error: Error +} + +export type Condition = + | GroupCondition + | EventTypeCondition + | EventCondition + | EventPropertyCondition + | EventContextCondition + +export type GroupConditionOperator = 'and' | 'or' + +export interface EventTypeCondition { + type: 'event-type' + operator: Operator + value?: string +} + +export interface EventCondition { + type: 'event' + operator: Operator + value?: string +} + +export interface EventPropertyCondition { + type: 'event-property' + name: string + operator: Operator + value?: string | boolean | number +} + +export interface EventContextCondition { + type: 'event-context' + name: string + operator: Operator + value?: string | boolean | number +} + +export type Operator = + | '=' + | '!=' + | '<' + | '<=' + | '>' + | '>=' + | 'contains' + | 'not_contains' + | 'starts_with' + | 'not_starts_with' + | 'ends_with' + | 'not_ends_with' + | 'exists' + | 'not_exists' + +export type ConditionType = 'group' | 'event-type' | 'event' | 'event-property' | 'event-context' + +export type PropertyConditionType = 'event-property' | 'event-context' diff --git a/packages/destination-subscriptions/src/validate.ts b/packages/destination-subscriptions/src/validate.ts new file mode 100644 index 0000000000..c660ec520b --- /dev/null +++ b/packages/destination-subscriptions/src/validate.ts @@ -0,0 +1,90 @@ +import { get } from './get' +import { Subscription, Condition, GroupCondition, Operator, ErrorCondition } from './types' + +const validateGroupCondition = (condition: GroupCondition, data: unknown): boolean => { + if (condition.operator === 'and') { + return condition.children.every((childCondition) => { + return validateCondition(childCondition, data) + }) + } + + if (condition.operator === 'or') { + return condition.children.some((childCondition) => { + return validateCondition(childCondition, data) + }) + } + + return false +} + +const validate = (condition: Subscription, data: unknown): boolean => { + if ((condition as ErrorCondition).error || typeof data === 'undefined') { + return false + } + + return validateGroupCondition(condition as GroupCondition, data) +} + +export default validate + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const validateCondition = (condition: Condition, data: any): boolean => { + if (condition.type === 'event-type') { + return validateValue(data.type, condition.operator, condition.value) + } + + if (condition.type === 'event') { + return validateValue(data.event, condition.operator, condition.value) + } + + if (condition.type === 'event-property') { + const properties = ['identify', 'group'].includes(data.type) ? data.traits : data.properties + + return validateValue(get(properties, condition.name), condition.operator, condition.value) + } + + if (condition.type === 'event-context') { + return validateValue(get(data.context, condition.name), condition.operator, condition.value) + } + + if (condition.type === 'group') { + return validateGroupCondition(condition, data) + } + + return false +} + +const validateValue = (actual: unknown, operator: Operator, expected?: string | boolean | number): boolean => { + switch (operator) { + case '=': + return String(actual) === String(expected) + case '!=': + return String(actual) !== String(expected) + case '<': + return Number(actual) < Number(expected) + case '<=': + return Number(actual) <= Number(expected) + case '>': + return Number(actual) > Number(expected) + case '>=': + return Number(actual) >= Number(expected) + case 'contains': + return typeof actual === 'string' && actual.includes(String(expected)) + case 'not_contains': + return typeof actual === 'string' && !actual.includes(String(expected)) + case 'starts_with': + return typeof actual === 'string' && actual.startsWith(String(expected)) + case 'not_starts_with': + return typeof actual === 'string' && !actual.startsWith(String(expected)) + case 'ends_with': + return typeof actual === 'string' && actual.endsWith(String(expected)) + case 'not_ends_with': + return typeof actual === 'string' && !actual.endsWith(String(expected)) + case 'exists': + return actual !== undefined && actual !== null + case 'not_exists': + return actual === undefined || actual === null + default: + return false + } +} diff --git a/packages/destination-subscriptions/tsconfig.build.json b/packages/destination-subscriptions/tsconfig.build.json new file mode 100644 index 0000000000..982a7807e2 --- /dev/null +++ b/packages/destination-subscriptions/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "exclude": ["**/__tests__/**/*.ts"], + "include": ["src"] +} diff --git a/packages/destination-subscriptions/tsconfig.cjs.json b/packages/destination-subscriptions/tsconfig.cjs.json new file mode 100644 index 0000000000..1105a7620d --- /dev/null +++ b/packages/destination-subscriptions/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "dist/cjs" + } +} diff --git a/packages/destination-subscriptions/tsconfig.json b/packages/destination-subscriptions/tsconfig.json new file mode 100644 index 0000000000..394070db6f --- /dev/null +++ b/packages/destination-subscriptions/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "module": "es2020", + "outDir": "dist/esm", + "target": "es2018", + "lib": ["es2018"], + "esModuleInterop": true + }, + "exclude": [], + "include": ["src"] +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000000..a5e3e94152 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/test/**/*.ts", "scripts/**/*.ts", "**/__tests__/**/*.ts", ".eslintrc.js"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..e430b65b36 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "lib": ["es2018"], + "declaration": true, + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "sourceMap": true, + "removeComments": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true, + "skipLibCheck": true + }, + "exclude": ["node_modules", "dist", "templates"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..2cba17bbea --- /dev/null +++ b/yarn.lock @@ -0,0 +1,13706 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apidevtools/json-schema-ref-parser@9.0.9": + version "9.0.9" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" + integrity sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@arrows/array@^1.4.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@arrows/array/-/array-1.4.1.tgz#a6580a08cee219755ca9a8eb14e956d3c29a5508" + integrity sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g== + dependencies: + "@arrows/composition" "^1.2.2" + +"@arrows/composition@^1.0.0", "@arrows/composition@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@arrows/composition/-/composition-1.2.2.tgz#d0a213cac8f8c36c1c75856a1e6ed940c27e9169" + integrity sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ== + +"@arrows/dispatch@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@arrows/dispatch/-/dispatch-1.0.3.tgz#c4c06260f89e9dd4ce280df3712980aa2f3de976" + integrity sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw== + dependencies: + "@arrows/composition" "^1.2.2" + +"@arrows/error@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@arrows/error/-/error-1.0.2.tgz#4e68036f901118ba6f1de88656ef6be49e650414" + integrity sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA== + +"@arrows/multimethod@^1.1.6": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@arrows/multimethod/-/multimethod-1.1.7.tgz#bc7c26c3aa7703fc967e65da4f00718b1428eb4a" + integrity sha512-EjHD3XuGAV4G28rm7mu8k7zQJh/EOizh104/p9i2ofGcnL5mgKONFH/Bq6H3SJjM+WDAlKcR9WBpNhaAKCnH2g== + dependencies: + "@arrows/array" "^1.4.0" + "@arrows/composition" "^1.2.2" + "@arrows/error" "^1.0.2" + fast-deep-equal "^3.1.1" + +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.13.11": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" + integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== + +"@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" + integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== + +"@babel/core@^7.1.0", "@babel/core@^7.13.16", "@babel/core@^7.7.5": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" + integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helpers" "^7.14.6" + "@babel/parser" "^7.14.6" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.2.tgz#d5773e8b557d421fd6ce0d5efa5fd7fc22567c30" + integrity sha512-OnADYbKrffDVai5qcpkMxQ7caomHOoEwjkouqnN2QhydAjowFAZcsdecFIRUBdb+ZcruwYE4ythYmF1UBZU5xQ== + dependencies: + "@babel/types" "^7.14.2" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== + dependencies: + "@babel/types" "^7.14.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" + integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" + integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== + dependencies: + "@babel/compat-data" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" + integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + +"@babel/helper-create-regexp-features-plugin@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" + integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + regexpu-core "^4.7.1" + +"@babel/helper-define-polyfill-provider@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" + integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-explode-assignable-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" + integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-function-name@^7.14.2", "@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-member-expression-to-functions@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" + integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-imports@^7.12.13": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== + dependencies: + "@babel/types" "^7.13.12" + +"@babel/helper-module-imports@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-transforms@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" + integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-optimise-call-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" + integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + +"@babel/helper-plugin-utils@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-remap-async-to-generator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz#51439c913612958f54a987a4ffc9ee587a2045d6" + integrity sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-wrap-function" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-replace-supers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" + integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-simple-access@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" + integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" + integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.0", "@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/helper-wrap-function@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff" + integrity sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ== + dependencies: + "@babel/helper-function-name" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helpers@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" + integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== + dependencies: + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/highlight@^7.10.4": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.16", "@babel/parser@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" + integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ== + +"@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e" + integrity sha512-ZoJS2XCKPBfTmL122iP6NM9dOg+d4lc9fFk3zxc8iDjvt8Pk4+TlsHSKhIPf6X+L5ORCdBzqMZDjL/WHj7WknQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/plugin-proposal-optional-chaining" "^7.14.5" + +"@babel/plugin-proposal-async-generator-functions@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz#784a48c3d8ed073f65adcf30b57bcbf6c8119ace" + integrity sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-remap-async-to-generator" "^7.14.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" + integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-proposal-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.5.tgz#158e9e10d449c3849ef3ecde94a03d9f1841b681" + integrity sha512-KBAH5ksEnYHCegqseI5N9skTdxgJdmDoAOc0uXa+4QMYKeZD0w5IARh4FMlTNtaHhbB8v+KzMdTgxMMzsIy6Yg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c" + integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76" + integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb" + integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738" + integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" + integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18" + integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363" + integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g== + dependencies: + "@babel/compat-data" "^7.14.7" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.14.5" + +"@babel/plugin-proposal-optional-catch-binding@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" + integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" + integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d" + integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-proposal-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.5.tgz#9f65a4d0493a940b4c01f8aa9d3f1894a587f636" + integrity sha512-62EyfyA3WA0mZiF2e2IV9mc9Ghwxcg8YTu8BS4Wss4Y3PY725OmS9M0qLORbJwLqFtGh+jiE4wAmocK2CTUK2Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-create-class-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" + integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" + integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-arrow-functions@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" + integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-async-to-generator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67" + integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-remap-async-to-generator" "^7.14.5" + +"@babel/plugin-transform-block-scoped-functions@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" + integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-block-scoping@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" + integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-classes@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" + integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" + integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-destructuring@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" + integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" + integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-duplicate-keys@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954" + integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-exponentiation-operator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" + integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-flow-strip-types@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz#0dc9c1d11dcdc873417903d6df4bed019ef0f85e" + integrity sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-flow" "^7.14.5" + +"@babel/plugin-transform-for-of@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" + integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" + integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== + dependencies: + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-literals@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" + integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-member-expression-literals@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" + integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-modules-amd@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7" + integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g== + dependencies: + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" + integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== + dependencies: + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz#c75342ef8b30dcde4295d3401aae24e65638ed29" + integrity sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA== + dependencies: + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.5" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0" + integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA== + dependencies: + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz#60c06892acf9df231e256c24464bfecb0908fd4e" + integrity sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.14.5" + +"@babel/plugin-transform-new-target@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8" + integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-object-super@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" + integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + +"@babel/plugin-transform-parameters@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" + integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" + integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-regenerator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" + integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304" + integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-shorthand-properties@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" + integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-spread@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" + integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + +"@babel/plugin-transform-sticky-regex@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" + integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-template-literals@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" + integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-typeof-symbol@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4" + integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-typescript@^7.14.5": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c" + integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.14.6" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-typescript" "^7.14.5" + +"@babel/plugin-transform-unicode-escapes@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" + integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-unicode-regex@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" + integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/preset-env@^7.13.10": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.7.tgz#5c70b22d4c2d893b03d8c886a5c17422502b932a" + integrity sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA== + dependencies: + "@babel/compat-data" "^7.14.7" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5" + "@babel/plugin-proposal-async-generator-functions" "^7.14.7" + "@babel/plugin-proposal-class-properties" "^7.14.5" + "@babel/plugin-proposal-class-static-block" "^7.14.5" + "@babel/plugin-proposal-dynamic-import" "^7.14.5" + "@babel/plugin-proposal-export-namespace-from" "^7.14.5" + "@babel/plugin-proposal-json-strings" "^7.14.5" + "@babel/plugin-proposal-logical-assignment-operators" "^7.14.5" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" + "@babel/plugin-proposal-numeric-separator" "^7.14.5" + "@babel/plugin-proposal-object-rest-spread" "^7.14.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.14.5" + "@babel/plugin-proposal-optional-chaining" "^7.14.5" + "@babel/plugin-proposal-private-methods" "^7.14.5" + "@babel/plugin-proposal-private-property-in-object" "^7.14.5" + "@babel/plugin-proposal-unicode-property-regex" "^7.14.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.14.5" + "@babel/plugin-transform-async-to-generator" "^7.14.5" + "@babel/plugin-transform-block-scoped-functions" "^7.14.5" + "@babel/plugin-transform-block-scoping" "^7.14.5" + "@babel/plugin-transform-classes" "^7.14.5" + "@babel/plugin-transform-computed-properties" "^7.14.5" + "@babel/plugin-transform-destructuring" "^7.14.7" + "@babel/plugin-transform-dotall-regex" "^7.14.5" + "@babel/plugin-transform-duplicate-keys" "^7.14.5" + "@babel/plugin-transform-exponentiation-operator" "^7.14.5" + "@babel/plugin-transform-for-of" "^7.14.5" + "@babel/plugin-transform-function-name" "^7.14.5" + "@babel/plugin-transform-literals" "^7.14.5" + "@babel/plugin-transform-member-expression-literals" "^7.14.5" + "@babel/plugin-transform-modules-amd" "^7.14.5" + "@babel/plugin-transform-modules-commonjs" "^7.14.5" + "@babel/plugin-transform-modules-systemjs" "^7.14.5" + "@babel/plugin-transform-modules-umd" "^7.14.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.7" + "@babel/plugin-transform-new-target" "^7.14.5" + "@babel/plugin-transform-object-super" "^7.14.5" + "@babel/plugin-transform-parameters" "^7.14.5" + "@babel/plugin-transform-property-literals" "^7.14.5" + "@babel/plugin-transform-regenerator" "^7.14.5" + "@babel/plugin-transform-reserved-words" "^7.14.5" + "@babel/plugin-transform-shorthand-properties" "^7.14.5" + "@babel/plugin-transform-spread" "^7.14.6" + "@babel/plugin-transform-sticky-regex" "^7.14.5" + "@babel/plugin-transform-template-literals" "^7.14.5" + "@babel/plugin-transform-typeof-symbol" "^7.14.5" + "@babel/plugin-transform-unicode-escapes" "^7.14.5" + "@babel/plugin-transform-unicode-regex" "^7.14.5" + "@babel/preset-modules" "^0.1.4" + "@babel/types" "^7.14.5" + babel-plugin-polyfill-corejs2 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-regenerator "^0.2.2" + core-js-compat "^3.15.0" + semver "^6.3.0" + +"@babel/preset-flow@^7.13.13": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.14.5.tgz#a1810b0780c8b48ab0bece8e7ab8d0d37712751c" + integrity sha512-pP5QEb4qRUSVGzzKx9xqRuHUrM/jEzMqdrZpdMA+oUCRgd5zM1qGr5y5+ZgAL/1tVv1H0dyk5t4SKJntqyiVtg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-flow-strip-types" "^7.14.5" + +"@babel/preset-modules@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-typescript@^7.13.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0" + integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-typescript" "^7.14.5" + +"@babel/register@^7.13.16": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.14.5.tgz#d0eac615065d9c2f1995842f85d6e56c345f3233" + integrity sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/runtime-corejs3@^7.12.1": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz#0ef292bbce40ca00f874c9724ef175a12476465c" + integrity sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA== + dependencies: + core-js-pure "^3.15.0" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.8.4": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/template@^7.3.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.4.5": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" + integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.14.2" + "@babel/helper-function-name" "^7.14.2" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.14.2" + "@babel/types" "^7.14.2" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.7" + "@babel/types" "^7.14.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.14.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" + integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + to-fast-properties "^2.0.0" + +"@babel/types@^7.13.12", "@babel/types@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" + integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== + +"@eslint/eslintrc@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" + integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@fullstory/browser@^1.4.9": + version "1.4.9" + resolved "https://registry.yarnpkg.com/@fullstory/browser/-/browser-1.4.9.tgz#e350d779caa1ba56bed22615d9b157efbefa7b47" + integrity sha512-h8ihrXT8pGemh5n7CKrukkEbbRIuCi0I/GJKI8DJpGyloI4WNTX5SC8Aihec7ScfK6Fi6ZpiLkGP3hogZqoNWw== + +"@hapi/hoek@^9.0.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" + integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== + dependencies: + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@lerna/add@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" + integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== + dependencies: + "@lerna/bootstrap" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + npm-package-arg "^8.1.0" + p-map "^4.0.0" + pacote "^11.2.6" + semver "^7.3.4" + +"@lerna/bootstrap@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" + integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/has-npm-version" "4.0.0" + "@lerna/npm-install" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/rimraf-dir" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/symlink-binary" "4.0.0" + "@lerna/symlink-dependencies" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + get-port "^5.1.1" + multimatch "^5.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + p-map "^4.0.0" + p-map-series "^2.1.0" + p-waterfall "^2.1.1" + read-package-tree "^5.3.1" + semver "^7.3.4" + +"@lerna/changed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" + integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== + dependencies: + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/listable" "4.0.0" + "@lerna/output" "4.0.0" + +"@lerna/check-working-tree@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" + integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== + dependencies: + "@lerna/collect-uncommitted" "4.0.0" + "@lerna/describe-ref" "4.0.0" + "@lerna/validation-error" "4.0.0" + +"@lerna/child-process@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" + integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== + dependencies: + chalk "^4.1.0" + execa "^5.0.0" + strong-log-transformer "^2.1.0" + +"@lerna/clean@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" + integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/rimraf-dir" "4.0.0" + p-map "^4.0.0" + p-map-series "^2.1.0" + p-waterfall "^2.1.1" + +"@lerna/cli@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" + integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== + dependencies: + "@lerna/global-options" "4.0.0" + dedent "^0.7.0" + npmlog "^4.1.2" + yargs "^16.2.0" + +"@lerna/collect-uncommitted@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" + integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== + dependencies: + "@lerna/child-process" "4.0.0" + chalk "^4.1.0" + npmlog "^4.1.2" + +"@lerna/collect-updates@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" + integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/describe-ref" "4.0.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + slash "^3.0.0" + +"@lerna/command@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" + integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/project" "4.0.0" + "@lerna/validation-error" "4.0.0" + "@lerna/write-log-file" "4.0.0" + clone-deep "^4.0.1" + dedent "^0.7.0" + execa "^5.0.0" + is-ci "^2.0.0" + npmlog "^4.1.2" + +"@lerna/conventional-commits@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" + integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== + dependencies: + "@lerna/validation-error" "4.0.0" + conventional-changelog-angular "^5.0.12" + conventional-changelog-core "^4.2.2" + conventional-recommended-bump "^6.1.0" + fs-extra "^9.1.0" + get-stream "^6.0.0" + lodash.template "^4.5.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + pify "^5.0.0" + semver "^7.3.4" + +"@lerna/create-symlink@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" + integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== + dependencies: + cmd-shim "^4.1.0" + fs-extra "^9.1.0" + npmlog "^4.1.2" + +"@lerna/create@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" + integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + fs-extra "^9.1.0" + globby "^11.0.2" + init-package-json "^2.0.2" + npm-package-arg "^8.1.0" + p-reduce "^2.1.0" + pacote "^11.2.6" + pify "^5.0.0" + semver "^7.3.4" + slash "^3.0.0" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + whatwg-url "^8.4.0" + yargs-parser "20.2.4" + +"@lerna/describe-ref@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" + integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== + dependencies: + "@lerna/child-process" "4.0.0" + npmlog "^4.1.2" + +"@lerna/diff@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" + integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/validation-error" "4.0.0" + npmlog "^4.1.2" + +"@lerna/exec@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" + integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/profiler" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + p-map "^4.0.0" + +"@lerna/filter-options@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" + integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== + dependencies: + "@lerna/collect-updates" "4.0.0" + "@lerna/filter-packages" "4.0.0" + dedent "^0.7.0" + npmlog "^4.1.2" + +"@lerna/filter-packages@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" + integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== + dependencies: + "@lerna/validation-error" "4.0.0" + multimatch "^5.0.0" + npmlog "^4.1.2" + +"@lerna/get-npm-exec-opts@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" + integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== + dependencies: + npmlog "^4.1.2" + +"@lerna/get-packed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" + integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== + dependencies: + fs-extra "^9.1.0" + ssri "^8.0.1" + tar "^6.1.0" + +"@lerna/github-client@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" + integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== + dependencies: + "@lerna/child-process" "4.0.0" + "@octokit/plugin-enterprise-rest" "^6.0.1" + "@octokit/rest" "^18.1.0" + git-url-parse "^11.4.4" + npmlog "^4.1.2" + +"@lerna/gitlab-client@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" + integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== + dependencies: + node-fetch "^2.6.1" + npmlog "^4.1.2" + whatwg-url "^8.4.0" + +"@lerna/global-options@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" + integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== + +"@lerna/has-npm-version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" + integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== + dependencies: + "@lerna/child-process" "4.0.0" + semver "^7.3.4" + +"@lerna/import@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" + integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/validation-error" "4.0.0" + dedent "^0.7.0" + fs-extra "^9.1.0" + p-map-series "^2.1.0" + +"@lerna/info@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" + integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/output" "4.0.0" + envinfo "^7.7.4" + +"@lerna/init@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" + integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/command" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + write-json-file "^4.3.0" + +"@lerna/link@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" + integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/package-graph" "4.0.0" + "@lerna/symlink-dependencies" "4.0.0" + p-map "^4.0.0" + slash "^3.0.0" + +"@lerna/list@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" + integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/listable" "4.0.0" + "@lerna/output" "4.0.0" + +"@lerna/listable@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" + integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== + dependencies: + "@lerna/query-graph" "4.0.0" + chalk "^4.1.0" + columnify "^1.5.4" + +"@lerna/log-packed@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" + integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== + dependencies: + byte-size "^7.0.0" + columnify "^1.5.4" + has-unicode "^2.0.1" + npmlog "^4.1.2" + +"@lerna/npm-conf@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" + integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== + dependencies: + config-chain "^1.1.12" + pify "^5.0.0" + +"@lerna/npm-dist-tag@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" + integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== + dependencies: + "@lerna/otplease" "4.0.0" + npm-package-arg "^8.1.0" + npm-registry-fetch "^9.0.0" + npmlog "^4.1.2" + +"@lerna/npm-install@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" + integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/get-npm-exec-opts" "4.0.0" + fs-extra "^9.1.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + signal-exit "^3.0.3" + write-pkg "^4.0.0" + +"@lerna/npm-publish@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" + integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== + dependencies: + "@lerna/otplease" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + fs-extra "^9.1.0" + libnpmpublish "^4.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + pify "^5.0.0" + read-package-json "^3.0.0" + +"@lerna/npm-run-script@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" + integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== + dependencies: + "@lerna/child-process" "4.0.0" + "@lerna/get-npm-exec-opts" "4.0.0" + npmlog "^4.1.2" + +"@lerna/otplease@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" + integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== + dependencies: + "@lerna/prompt" "4.0.0" + +"@lerna/output@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" + integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== + dependencies: + npmlog "^4.1.2" + +"@lerna/pack-directory@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" + integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== + dependencies: + "@lerna/get-packed" "4.0.0" + "@lerna/package" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + npm-packlist "^2.1.4" + npmlog "^4.1.2" + tar "^6.1.0" + temp-write "^4.0.0" + +"@lerna/package-graph@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" + integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== + dependencies: + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/validation-error" "4.0.0" + npm-package-arg "^8.1.0" + npmlog "^4.1.2" + semver "^7.3.4" + +"@lerna/package@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" + integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== + dependencies: + load-json-file "^6.2.0" + npm-package-arg "^8.1.0" + write-pkg "^4.0.0" + +"@lerna/prerelease-id-from-version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" + integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== + dependencies: + semver "^7.3.4" + +"@lerna/profiler@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" + integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== + dependencies: + fs-extra "^9.1.0" + npmlog "^4.1.2" + upath "^2.0.1" + +"@lerna/project@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" + integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== + dependencies: + "@lerna/package" "4.0.0" + "@lerna/validation-error" "4.0.0" + cosmiconfig "^7.0.0" + dedent "^0.7.0" + dot-prop "^6.0.1" + glob-parent "^5.1.1" + globby "^11.0.2" + load-json-file "^6.2.0" + npmlog "^4.1.2" + p-map "^4.0.0" + resolve-from "^5.0.0" + write-json-file "^4.3.0" + +"@lerna/prompt@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" + integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== + dependencies: + inquirer "^7.3.3" + npmlog "^4.1.2" + +"@lerna/publish@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" + integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== + dependencies: + "@lerna/check-working-tree" "4.0.0" + "@lerna/child-process" "4.0.0" + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/describe-ref" "4.0.0" + "@lerna/log-packed" "4.0.0" + "@lerna/npm-conf" "4.0.0" + "@lerna/npm-dist-tag" "4.0.0" + "@lerna/npm-publish" "4.0.0" + "@lerna/otplease" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/pack-directory" "4.0.0" + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/pulse-till-done" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + "@lerna/version" "4.0.0" + fs-extra "^9.1.0" + libnpmaccess "^4.0.1" + npm-package-arg "^8.1.0" + npm-registry-fetch "^9.0.0" + npmlog "^4.1.2" + p-map "^4.0.0" + p-pipe "^3.1.0" + pacote "^11.2.6" + semver "^7.3.4" + +"@lerna/pulse-till-done@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" + integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== + dependencies: + npmlog "^4.1.2" + +"@lerna/query-graph@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" + integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== + dependencies: + "@lerna/package-graph" "4.0.0" + +"@lerna/resolve-symlink@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" + integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== + dependencies: + fs-extra "^9.1.0" + npmlog "^4.1.2" + read-cmd-shim "^2.0.0" + +"@lerna/rimraf-dir@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" + integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== + dependencies: + "@lerna/child-process" "4.0.0" + npmlog "^4.1.2" + path-exists "^4.0.0" + rimraf "^3.0.2" + +"@lerna/run-lifecycle@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" + integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== + dependencies: + "@lerna/npm-conf" "4.0.0" + npm-lifecycle "^3.1.5" + npmlog "^4.1.2" + +"@lerna/run-topologically@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" + integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== + dependencies: + "@lerna/query-graph" "4.0.0" + p-queue "^6.6.2" + +"@lerna/run@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" + integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== + dependencies: + "@lerna/command" "4.0.0" + "@lerna/filter-options" "4.0.0" + "@lerna/npm-run-script" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/profiler" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/timer" "4.0.0" + "@lerna/validation-error" "4.0.0" + p-map "^4.0.0" + +"@lerna/symlink-binary@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" + integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== + dependencies: + "@lerna/create-symlink" "4.0.0" + "@lerna/package" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + +"@lerna/symlink-dependencies@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" + integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== + dependencies: + "@lerna/create-symlink" "4.0.0" + "@lerna/resolve-symlink" "4.0.0" + "@lerna/symlink-binary" "4.0.0" + fs-extra "^9.1.0" + p-map "^4.0.0" + p-map-series "^2.1.0" + +"@lerna/timer@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" + integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== + +"@lerna/validation-error@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" + integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== + dependencies: + npmlog "^4.1.2" + +"@lerna/version@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" + integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== + dependencies: + "@lerna/check-working-tree" "4.0.0" + "@lerna/child-process" "4.0.0" + "@lerna/collect-updates" "4.0.0" + "@lerna/command" "4.0.0" + "@lerna/conventional-commits" "4.0.0" + "@lerna/github-client" "4.0.0" + "@lerna/gitlab-client" "4.0.0" + "@lerna/output" "4.0.0" + "@lerna/prerelease-id-from-version" "4.0.0" + "@lerna/prompt" "4.0.0" + "@lerna/run-lifecycle" "4.0.0" + "@lerna/run-topologically" "4.0.0" + "@lerna/validation-error" "4.0.0" + chalk "^4.1.0" + dedent "^0.7.0" + load-json-file "^6.2.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + p-map "^4.0.0" + p-pipe "^3.1.0" + p-reduce "^2.1.0" + p-waterfall "^2.1.1" + semver "^7.3.4" + slash "^3.0.0" + temp-write "^4.0.0" + write-json-file "^4.3.0" + +"@lerna/write-log-file@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" + integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== + dependencies: + npmlog "^4.1.2" + write-file-atomic "^3.0.3" + +"@lukeed/csprng@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.0.0.tgz#733a122382749d27e2e46ec38f8c71c9d53a9636" + integrity sha512-ruuGHsnabmObBdeMg3vKdGRmh06Oog3eFpf/Tk6X0kDSJDpJTDCj2dqdp1+0VjzIUgHlFF9GBm7uFqfYhhdX9g== + +"@lukeed/uuid@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@lukeed/uuid/-/uuid-2.0.0.tgz#1c0f33c071cb6902bc3b9e475782ada7314ef9bd" + integrity sha512-dUz8OmYvlY5A9wXaroHIMSPASpSYRLCqbPvxGSyHguhtTQIy24lC+EGxQlwv71AhRCO55WOtgwhzQLpw27JaJQ== + dependencies: + "@lukeed/csprng" "^1.0.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" + integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@npmcli/ci-detect@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + +"@npmcli/git@^2.0.1": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== + dependencies: + "@npmcli/promise-spawn" "^1.3.2" + lru-cache "^6.0.0" + mkdirp "^1.0.4" + npm-pick-manifest "^6.1.1" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.6": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== + dependencies: + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@npmcli/node-gyp@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" + integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== + +"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== + dependencies: + infer-owner "^1.0.4" + +"@npmcli/run-script@^1.8.2": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.5.tgz#f250a0c5e1a08a792d775a315d0ff42fc3a51e1d" + integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== + dependencies: + "@npmcli/node-gyp" "^1.0.2" + "@npmcli/promise-spawn" "^1.3.2" + infer-owner "^1.0.4" + node-gyp "^7.1.0" + read-package-json-fast "^2.0.1" + +"@oclif/command@^1", "@oclif/command@^1.5.20", "@oclif/command@^1.6.0", "@oclif/command@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.8.0.tgz#c1a499b10d26e9d1a611190a81005589accbb339" + integrity sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw== + dependencies: + "@oclif/config" "^1.15.1" + "@oclif/errors" "^1.3.3" + "@oclif/parser" "^3.8.3" + "@oclif/plugin-help" "^3" + debug "^4.1.1" + semver "^7.3.2" + +"@oclif/config@^1", "@oclif/config@^1.15.1", "@oclif/config@^1.17.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.17.0.tgz#ba8639118633102a7e481760c50054623d09fcab" + integrity sha512-Lmfuf6ubjQ4ifC/9bz1fSCHc6F6E653oyaRXxg+lgT4+bYf9bk+nqrUpAbrXyABkCqgIBiFr3J4zR/kiFdE1PA== + dependencies: + "@oclif/errors" "^1.3.3" + "@oclif/parser" "^3.8.0" + debug "^4.1.1" + globby "^11.0.1" + is-wsl "^2.1.1" + tslib "^2.0.0" + +"@oclif/dev-cli@^1": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@oclif/dev-cli/-/dev-cli-1.26.0.tgz#e3ec294b362c010ffc8948003d3770955c7951fd" + integrity sha512-272udZP+bG4qahoAcpWcMTJKiA+V42kRMqQM7n4tgW35brYb2UP5kK+p08PpF8sgSfRTV8MoJVJG9ax5kY82PA== + dependencies: + "@oclif/command" "^1.8.0" + "@oclif/config" "^1.17.0" + "@oclif/errors" "^1.3.3" + "@oclif/plugin-help" "^3.2.0" + cli-ux "^5.2.1" + debug "^4.1.1" + find-yarn-workspace-root "^2.0.0" + fs-extra "^8.1" + github-slugger "^1.2.1" + lodash "^4.17.11" + normalize-package-data "^3.0.0" + qqjs "^0.3.10" + tslib "^2.0.3" + +"@oclif/errors@^1", "@oclif/errors@^1.2.1", "@oclif/errors@^1.2.2", "@oclif/errors@^1.3.3": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@oclif/errors/-/errors-1.3.4.tgz#a96f94536b4e25caa72eff47e8b3ed04f6995f55" + integrity sha512-pJKXyEqwdfRTUdM8n5FIHiQQHg5ETM0Wlso8bF9GodczO40mF5Z3HufnYWJE7z8sGKxOeJCdbAVZbS8Y+d5GCw== + dependencies: + clean-stack "^3.0.0" + fs-extra "^8.1" + indent-string "^4.0.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +"@oclif/linewrap@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@oclif/linewrap/-/linewrap-1.0.0.tgz#aedcb64b479d4db7be24196384897b5000901d91" + integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== + +"@oclif/parser@^3.8.0", "@oclif/parser@^3.8.3": + version "3.8.5" + resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.5.tgz#c5161766a1efca7343e1f25d769efbefe09f639b" + integrity sha512-yojzeEfmSxjjkAvMRj0KzspXlMjCfBzNRPkWw8ZwOSoNWoJn+OCS/m/S+yfV6BvAM4u2lTzX9Y5rCbrFIgkJLg== + dependencies: + "@oclif/errors" "^1.2.2" + "@oclif/linewrap" "^1.0.0" + chalk "^2.4.2" + tslib "^1.9.3" + +"@oclif/plugin-help@^3", "@oclif/plugin-help@^3.2.0": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.2.2.tgz#063ee08cee556573a5198fbdfdaa32796deba0ed" + integrity sha512-SPZ8U8PBYK0n4srFjCLedk0jWU4QlxgEYLCXIBShJgOwPhTTQknkUlsEwaMIevvCU4iCQZhfMX+D8Pz5GZjFgA== + dependencies: + "@oclif/command" "^1.5.20" + "@oclif/config" "^1.15.1" + "@oclif/errors" "^1.2.2" + chalk "^4.1.0" + indent-string "^4.0.0" + lodash.template "^4.4.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + widest-line "^3.1.0" + wrap-ansi "^4.0.0" + +"@oclif/screen@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" + integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== + +"@oclif/test@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@oclif/test/-/test-1.2.8.tgz#a5b2ebd747832217d9af65ac30b58780c4c17c5e" + integrity sha512-HCh0qPge1JCqTEw4s2ScnicEZd4Ro4/0VvdjpsfCiX6fuDV53fRZ2uqLTgxKGHrVoqOZnVrRZHyhFyEsFGs+zQ== + dependencies: + fancy-test "^1.4.3" + +"@octokit/auth-token@^2.4.4": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" + integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.5.0": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.0" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.12" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.4.tgz#0c3f5bed440822182e972317122acb65d311a5ed" + integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== + dependencies: + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^8.1.4": + version "8.1.4" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-8.1.4.tgz#20684667b50176b5d24cdb89799a8a12d38c3775" + integrity sha512-NnGr4NNDqO5wjSDJo5nxrGtzZUwoT23YasqK2H4Pav/6vSgeVTxuqCL9Aeh+cWfTxDomj1M4Os5BrXFsvl7qiQ== + +"@octokit/plugin-enterprise-rest@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== + +"@octokit/plugin-paginate-rest@^2.6.2": + version "2.13.6" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.6.tgz#03633839b0ec57a108d2ca1c4b1f64b749d3506c" + integrity sha512-ai7TNKLi8tGkDvLM7fm0X1fbIP9u1nfXnN49ZAw2PgSoQou9yixKn5c3m0awuLacbuX2aXEvJpv1gKm3jboabg== + dependencies: + "@octokit/types" "^6.17.3" + +"@octokit/plugin-request-log@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + +"@octokit/plugin-rest-endpoint-methods@5.3.6": + version "5.3.6" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.6.tgz#1cc2106e73f44432e6dad9e464a33db7b5930a03" + integrity sha512-uJi23hKKzFS+ACePgKYthSPyYkRgQgB36h07AZAS0H98x2AyjZy2ppr9Q36Faq4eoHRgrPnmf1suquMmymkN/Q== + dependencies: + "@octokit/types" "^6.17.3" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.0.tgz#6084861b6e4fa21dc40c8e2a739ec5eff597e672" + integrity sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.16.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + universal-user-agent "^6.0.0" + +"@octokit/rest@^18.1.0": + version "18.6.5" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.6.5.tgz#170d3daf314353151d148230878b8f0caea90515" + integrity sha512-sXO0EK00fbjVQichqizODH5DIvZCtmMrlPzxDwv/G6n95YzhzlUeShPYm9ADft4bqBJFKTfOb2LTR+jk/sAb2Q== + dependencies: + "@octokit/core" "^3.5.0" + "@octokit/plugin-paginate-rest" "^2.6.2" + "@octokit/plugin-request-log" "^1.0.2" + "@octokit/plugin-rest-endpoint-methods" "5.3.6" + +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.17.3": + version "6.17.4" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.17.4.tgz#5ec011bfe3d08b7605c7d92a15796c470ee541b6" + integrity sha512-Ghk/JC4zC/1al1GwH6p8jVX6pLdypSWmbnx6h79C/yo3DeaDd6MsNsBFlHu22KbkFh+CdcAzFqdP7UdPaPPmmA== + dependencies: + "@octokit/openapi-types" "^8.1.4" + +"@polka/url@^1.0.0-next.15": + version "1.0.0-next.15" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.15.tgz#6a9d143f7f4f49db2d782f9e1c8839a29b43ae23" + integrity sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA== + +"@segment/analytics-next@^1.19.1": + version "1.24.6" + resolved "https://registry.yarnpkg.com/@segment/analytics-next/-/analytics-next-1.24.6.tgz#8e087fbbfafb6b7c61391d6e7beacf5de4d1107e" + integrity sha512-jGcw4XdXUXNA+13D7EhpdwJb7ZIaORglMv9lI9S1ynQEySsAAgVTnRVYKKq2PRTXEFW9RJLawdt6QgPD9b3d6A== + dependencies: + "@lukeed/uuid" "^2.0.0" + "@segment/analytics.js-video-plugins" "github://segmentio/analytics.js-video-plugins" + "@segment/facade" "3.3.10" + "@segment/tsub" "^0.1.4" + dset "^2.1.0" + js-cookie "^2.2.1" + spark-md5 "^3.0.1" + unfetch "^4.1.0" + +"@segment/analytics.js-video-plugins@github://segmentio/analytics.js-video-plugins": + version "0.2.1" + resolved "git+ssh://git@github.com/segmentio/analytics.js-video-plugins.git#b9b52877c5a91091efb1a1a4e78f7b587e773e69" + dependencies: + unfetch "^3.1.1" + +"@segment/control-plane-service-client@git+ssh://git@github.com/segmentio/control-plane-service-js-client.git#master": + version "9.3.0" + resolved "git+ssh://git@github.com/segmentio/control-plane-service-js-client.git#08690d7f0372be0da8bb5ef78f9cafe4b8e97138" + dependencies: + "@segment/remote-service-client" "^2.4.0" + aws4 "^1.11.0" + flat "^5.0.2" + lodash "^4.17.15" + path-to-regexp "^6.1.0" + querystring "^0.2.0" + +"@segment/facade@3.3.10": + version "3.3.10" + resolved "https://registry.yarnpkg.com/@segment/facade/-/facade-3.3.10.tgz#94381079d326f8d4b2d11e0b2b1a85ca7aac28c4" + integrity sha512-Ed6hRwpNqwjsj3s5fXFqzoWwyB/foVGM+uT3Kl94Yd5BlZtYvmHxtX5gGsAPD49eazIPxLqJDZSHz/2tnokRTQ== + dependencies: + "@segment/isodate-traverse" "^1.1.1" + inherits "^2.0.4" + klona "^2.0.4" + new-date "^1.0.3" + obj-case "0.2.1" + +"@segment/fql@^1.9.1": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@segment/fql/-/fql-1.10.0.tgz#4e50d689c77e078887e673acda2b5fad384e461c" + integrity sha512-bQU4o+HC+Oo2fjdxBsxNlYw7EasxgufveKWeg2ORR8huIo6M47riV3oVo34bcLfXyEWsmWISvtgzRQOrI2XbHw== + +"@segment/isodate-traverse@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@segment/isodate-traverse/-/isodate-traverse-1.1.1.tgz#37e1a68b5e48a841260145f1be86d342995dfc64" + integrity sha512-+G6e1SgAUkcq0EDMi+SRLfT48TNlLPF3QnSgFGVs0V9F3o3fq/woQ2rHFlW20W0yy5NnCUH0QGU3Am2rZy/E3w== + dependencies: + "@segment/isodate" "^1.0.3" + +"@segment/isodate@1.0.3", "@segment/isodate@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@segment/isodate/-/isodate-1.0.3.tgz#f44e8202d5edd277ce822785239474b2c9411d4a" + integrity sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A== + +"@segment/remote-service-client@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@segment/remote-service-client/-/remote-service-client-2.4.1.tgz#f8847b3c9dadaa898e6fb2bb9a5abad6c8c3f976" + integrity sha512-xvFQ6GvVKV+Fm40iuurf/E0Qs0s3Y3bLP2Xo9MsAkbOiwoOkzs6QKwEDWzAwXWtQxKbuTyY9z7EcxBf5KUOUOQ== + dependencies: + "@types/node" ">=8.10" + decamelize "^3.2.0" + got "^9.0.0" + is-retry-allowed "^1.2.0" + joi "^17.4.0" + nanoid "^2.1.7" + query-string "^6.10.1" + +"@segment/tsub@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@segment/tsub/-/tsub-0.1.4.tgz#20b76729ab9d217cc60b3ab50059b123916370c8" + integrity sha512-DxiC/0+3ZzGD+8uKO6TqouO2Lyf5BvsX9iNfPe6+VYn7REOAu0zttTanx7uhU2G/iwFDRPsQSe1JAPxNW25UtA== + dependencies: + dlv "^1.1.3" + dset "^2.0.1" + math-float64-ldexp "^1.0.1" + tiny-hashes "^1.0.1" + +"@sideway/address@^4.1.0": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" + integrity sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@sindresorhus/tsconfig@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/tsconfig/-/tsconfig-0.7.0.tgz#8388e864b5e6d72547175ed98152b736df39b20a" + integrity sha512-on7+0FFpUH7+g5iLFkmk0oruqzE6uUnpKK0D6HM637aIvQ/mlXz33VlamsS4TSU6pifWSNIrUxrNKjAReU2Juw== + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/fake-timers@^7.1.0": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@size-limit/file@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@size-limit/file/-/file-4.12.0.tgz#50166eca7b9b5aa15f51a72b3d9d31e2d8530e6f" + integrity sha512-csgGSAG3s2y9eOl/taahojXY91AXpNgqLs9HJ5c/Qmrs+6UHgXbwJ4vo475NfZmt1Y9simircb1ygqupauNUyA== + dependencies: + semver "7.3.5" + +"@size-limit/preset-small-lib@^4.10.1": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@size-limit/preset-small-lib/-/preset-small-lib-4.12.0.tgz#0f7eb6ca7b7e6acb87b17bf6f7d9000946423cfc" + integrity sha512-jqUT2PiPN4Bf9aeUcTHt78+mELwo1x3K+w4grfNkzV/46j1rK3eXarls4qc0/FHolE76cLgNTLNjrX/Zb9c65w== + dependencies: + "@size-limit/file" "4.12.0" + "@size-limit/webpack" "4.12.0" + +"@size-limit/webpack@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@size-limit/webpack/-/webpack-4.12.0.tgz#65076310bbe4f043fad0a3af2bea0423573ff7ae" + integrity sha512-CtqSqxffexYmuAKeKAT2xcDmLwGChHXeq8Y9gjIe9vxfzUx1HU7Nmc0XyvsLGZG1qZO4TFuANlfQeavGY0jB6A== + dependencies: + css-loader "^5.2.6" + escape-string-regexp "^4.0.0" + file-loader "^6.2.0" + mkdirp "^1.0.4" + nanoid "^3.1.23" + optimize-css-assets-webpack-plugin "^6.0.0" + pnp-webpack-plugin "^1.6.4" + rimraf "^3.0.2" + style-loader "^2.0.0" + webpack "^4.44.1" + webpack-bundle-analyzer "^4.4.2" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@trysound/sax@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669" + integrity sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow== + +"@types/amplitude-js@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@types/amplitude-js/-/amplitude-js-7.0.1.tgz#c0b1bc6e508faa513a30ece580c300f0738a9449" + integrity sha512-xrmhX39GoSBIuOOPWYL5uvVLn6a5Cvea0wb6SkJNW9+2w5GCK6hT0vvMaH1GaaeSqvb1QFjHNsEgXuyQqML0sw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.1.14" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" + integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" + integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.0.tgz#a34277cf8acbd3185ea74129e1f100491eb1da7f" + integrity sha512-IilJZ1hJBUZwMOVDNTdflOOLzJB/ZtljYVa7k3gEZN/jqIJIPkWHC6dvbX+DD2CwZDHB9wAKzZPzzqMIkW37/w== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/btoa-lite@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/btoa-lite/-/btoa-lite-1.0.0.tgz#e190a5a548e0b348adb0df9ac7fa5f1151c7cca4" + integrity sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg== + +"@types/chai@*": + version "4.2.19" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.19.tgz#80f286b515897413c7a35bdda069cc80f2344233" + integrity sha512-jRJgpRBuY+7izT7/WNXP/LsMO9YonsstuL+xuvycDyESpoDoIAsMd7suwpB4h9oEWB+ZlPTqJJ8EHomzNhwTPQ== + +"@types/connect@*": + version "3.4.34" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" + integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" + integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "7.2.13" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.13.tgz#e0ca7219ba5ded402062ad6f926d491ebb29dd53" + integrity sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" + integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== + +"@types/estree@^0.0.48": + version "0.0.48" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" + integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew== + +"@types/express-serve-static-core@^4.17.18": + version "4.17.22" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz#e011c55de3f17ddf1161f790042a15c5a218744d" + integrity sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.11": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.12.tgz#4bc1bf3cd0cfe6d3f6f2853648b40db7d54de350" + integrity sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/fs-extra@^9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87" + integrity sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA== + dependencies: + "@types/node" "*" + +"@types/glob@*", "@types/glob@^7.1.1": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + +"@types/intercom-web@^2.8.11": + version "2.8.11" + resolved "https://registry.yarnpkg.com/@types/intercom-web/-/intercom-web-2.8.11.tgz#c8e342cfd890ef8b4016ec0508994b18907b5a0a" + integrity sha512-9oJQiACRVQ1A61CJ+oXcLywOAxar4X5s8Lau4SI+WvgWRCB83wfmMVjdYceexySVQH+i9FOVYNKpZixeENJNbQ== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^26.0.15", "@types/jest@^26.0.23": + version "26.0.23" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" + integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + +"@types/jscodeshift@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@types/jscodeshift/-/jscodeshift-0.11.0.tgz#7224cf1a4d0383b4fb2694ffed52f57b45c3325b" + integrity sha512-OcJgr5GznWCEx2Oeg4eMUZYwssTHFj/tU8grrNCKdFQtAEAa0ezDiPHbCdSkyWrRSurXrYbNbHdhxbbB76pXNg== + dependencies: + ast-types "^0.14.1" + recast "^0.20.3" + +"@types/jsdom@^16.2.10": + version "16.2.12" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.12.tgz#2d47f5c9f7e40b4d5d9b5f0534c736dc475f9569" + integrity sha512-rD68Q2XSKBYOl38Tb9jbPuqZeTtpw09dFeuKWi7yhlZVfY8rV4xnQmISNQQZfhkQJGpJWp2qCmmrWjhiqDFNvA== + dependencies: + "@types/node" "*" + "@types/parse5" "*" + "@types/tough-cookie" "*" + +"@types/json-diff@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@types/json-diff/-/json-diff-0.5.1.tgz#fcbff418cef55b324a8598f67bfd4e52949f2164" + integrity sha512-/XJ7OPKk+fJCJF93CCA1l2EojXd+dNysFNJN42igT96Xur0tmGQx6U+tm5boDVvgwU7M8ADaWaRuJZx98v2a9g== + +"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/lodash@*", "@types/lodash@^4.14.168": + version "4.14.170" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" + integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/minimatch@*", "@types/minimatch@^3.0.3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" + integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + +"@types/minimist@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" + integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== + +"@types/mustache@^4.1.0": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.1.1.tgz#fcfa2db0cee6261e66f2437dc2fe71e26c7856b4" + integrity sha512-Sm0NWeLhS2QL7NNGsXvO+Fgp7e3JLHCO6RS3RCnfjAnkw6Y1bsji/AGfISdQZDIR/AeOyzkrxRk9jBkl55zdJw== + +"@types/node@*", "@types/node@>=8.10": + version "15.12.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.5.tgz#9a78318a45d75c9523d2396131bd3cca54b2d185" + integrity sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg== + +"@types/node@^10.17.0": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@*": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.0.tgz#38590dc2c3cf5717154064e3ee9b6947ee21b299" + integrity sha512-oPwPSj4a1wu9rsXTEGIJz91ISU725t0BmSnUhb57sI+M8XEmvUop84lzuiYdq0Y5M6xLY8DBPg0C2xEQKLyvBA== + +"@types/prettier@^2.0.0", "@types/prettier@^2.1.5": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.0.tgz#2e8332cc7363f887d32ec5496b207d26ba8052bb" + integrity sha512-hkc1DATxFLQo4VxPDpMH1gCkPpBbpOoJ/4nhuXw4n63/0R6bCpQECj4+K226UJ4JO/eJQz+1mC2I7JsWanAdQw== + +"@types/prompts@^2.0.10": + version "2.0.13" + resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.13.tgz#007b0cb1731174ce7ea328e3cd4d84de9bb2e8ba" + integrity sha512-jwMOIGy49VruR/gYehhJYgpVzB+EVpEE7t7j9m1oTo4HMpOe7KmsyqdBuoxAzA5B4caUgx0cKrWr7wUEqMXJ7Q== + dependencies: + "@types/node" "*" + +"@types/qs@*": + version "6.9.6" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" + integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/rimraf@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f" + integrity sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" + +"@types/serve-static@*": + version "1.13.9" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" + integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/sinon@*": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.2.tgz#f360d2f189c0fd433d14aeb97b9d705d7e4cc0e4" + integrity sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw== + dependencies: + "@sinonjs/fake-timers" "^7.1.0" + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== + +"@types/to-title-case@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/to-title-case/-/to-title-case-1.0.0.tgz#37257e5293a54a92f29efc45b7ebc5f20807f32d" + integrity sha512-064+ER3yeChxTQwM0LTRTlG7GK6/HXM9ekX1y/KqJi0pvBBIrvUnYWZ2JQGivCAuw29CuLq0b6zUPL9FY/JMnQ== + +"@types/tough-cookie@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" + integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== + +"@types/yargs-parser@*": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + +"@types/yargs@^15.0.0": + version "15.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" + integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^4.14.0": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz#c045e440196ae45464e08e20c38aff5c3a825947" + integrity sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ== + dependencies: + "@typescript-eslint/experimental-utils" "4.28.1" + "@typescript-eslint/scope-manager" "4.28.1" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz#3869489dcca3c18523c46018b8996e15948dbadc" + integrity sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "4.28.1" + "@typescript-eslint/types" "4.28.1" + "@typescript-eslint/typescript-estree" "4.28.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/experimental-utils@^4.0.1": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" + integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@^4.14.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" + integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== + dependencies: + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" + debug "^4.1.1" + +"@typescript-eslint/scope-manager@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" + integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== + dependencies: + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" + +"@typescript-eslint/scope-manager@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz#fd3c20627cdc12933f6d98b386940d8d0ce8a991" + integrity sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA== + dependencies: + "@typescript-eslint/types" "4.28.1" + "@typescript-eslint/visitor-keys" "4.28.1" + +"@typescript-eslint/types@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" + integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== + +"@typescript-eslint/types@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.1.tgz#d0f2ecbef3684634db357b9bbfc97b94b828f83f" + integrity sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg== + +"@typescript-eslint/typescript-estree@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" + integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== + dependencies: + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/typescript-estree@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz#af882ae41740d1f268e38b4d0fad21e7e8d86a81" + integrity sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ== + dependencies: + "@typescript-eslint/types" "4.28.1" + "@typescript-eslint/visitor-keys" "4.28.1" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" + integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== + dependencies: + "@typescript-eslint/types" "4.23.0" + eslint-visitor-keys "^2.0.0" + +"@typescript-eslint/visitor-keys@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz#162a515ee255f18a6068edc26df793cdc1ec9157" + integrity sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg== + dependencies: + "@typescript-eslint/types" "4.28.1" + eslint-visitor-keys "^2.0.0" + +"@webassemblyjs/ast@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f" + integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c" + integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA== + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4" + integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642" + integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-numbers@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9" + integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1" + integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA== + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b" + integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf" + integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b" + integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf" + integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw== + +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78" + integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/helper-wasm-section" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-opt" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + "@webassemblyjs/wast-printer" "1.11.0" + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe" + integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978" + integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754" + integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e" + integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa" + integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ== + +"@webpack-cli/info@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b" + integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.1.tgz#b5fde2f0f79c1e120307c415a4c1d5eb15a6f278" + integrity sha512-4vSVUiOPJLmr45S8rMGy7WDvpWxfFxfP/Qx/cxZFCfvoypTYpPPL1X8VIZMe0WTA+Jr7blUxwUSEZNkjoMTgSw== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@zeit/schemas@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" + integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== + +JSONStream@^1.0.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc" + integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w== + +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^7.1.1, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.0.4, acorn@^8.2.1, acorn@^8.2.4: + version "8.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" + integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== + +add-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= + +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0, aggregate-error@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@6.12.6, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.6.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.0.tgz#60cc45d9c46a477d80d92c48076d972c342e5720" + integrity sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= + dependencies: + string-width "^2.0.0" + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^2.0.0, ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +aproba@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +arch@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arg@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" + integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-types@0.14.2, ast-types@^0.14.1: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.11.0, aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-polyfill-corejs2@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" + integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.2" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" + integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + core-js-compat "^3.14.0" + +babel-plugin-polyfill-regenerator@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" + integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +before-after-hook@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik= + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + +benny@^3.6.15: + version "3.6.15" + resolved "https://registry.yarnpkg.com/benny/-/benny-3.6.15.tgz#930826819b89546b274febe803da2d248a676caa" + integrity sha512-kq6XVGGYVou3Y8KNPs3SEF881vi5fJ8sIf9w69D2rreiNfRicWVWK6u6/mObMw6BiexoHHumtipn5gcu0Tngng== + dependencies: + "@arrows/composition" "^1.0.0" + "@arrows/dispatch" "^1.0.2" + "@arrows/multimethod" "^1.1.6" + benchmark "^2.1.4" + fs-extra "^9.0.1" + json2csv "^5.0.4" + kleur "^4.1.3" + log-update "^4.0.0" + prettier "^2.1.2" + stats-median "^1.0.1" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0, body-parser@^1.18.2: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +boxen@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +boxen@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.3.1.tgz#a7d898243ae622f7abb6bb604d740a76c6a5461b" + integrity sha1-p9iYJDrmIvertrtgTXQKdsalRhs= + dependencies: + chalk "^1.1.1" + filled-array "^1.0.0" + object-assign "^4.0.1" + repeating "^2.0.0" + string-width "^1.0.1" + widest-line "^1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + +buffer-from@1.x, buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= + +byte-size@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" + integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== + +bytes-iec@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bytes-iec/-/bytes-iec-3.1.1.tgz#94cd36bf95c2c22a82002c247df8772d1d591083" + integrity sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacache@^12.0.2: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^15.0.5: + version "15.0.6" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" + integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +cacache@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" + integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219: + version "1.0.30001241" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz#cd3fae47eb3d7691692b406568d7a3e5b23c7598" + integrity sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +capture-stack-trace@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" + integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== + +cardinal@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + dependencies: + ansicolors "~0.3.2" + redeyed "~2.1.0" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.0.0, chalk@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1, chokidar@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-job-number@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ci-job-number/-/ci-job-number-1.2.2.tgz#f4e5918fcaeeda95b604f214be7d7d4a961fe0c0" + integrity sha512-CLOGsVDrVamzv8sXJGaILUVI6dsuAkouJP/n6t+OxLPeeA4DDby7zn9SB6EUpa1H7oIKoE+rMmkW80zYsFfUjA== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +clean-stack@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" + integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== + dependencies: + escape-string-regexp "4.0.0" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= + +cli-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.0.tgz#11ecfb58a79278cf6035a60c54e338f9d837897c" + integrity sha512-a0VZ8LeraW0jTuCkuAGMNufareGHhyZU9z8OGsW0gXd1hZGi1SRuNRXdbGkraBBKnhyUhyebFWnRbp+dIn0f0A== + dependencies: + ansi-regex "^2.1.1" + d "^1.0.1" + es5-ext "^0.10.51" + es6-iterator "^2.0.3" + memoizee "^0.4.14" + timers-ext "^0.1.7" + +cli-color@~0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-0.1.7.tgz#adc3200fa471cc211b0da7f566b71e98b9d67347" + integrity sha1-rcMgD6RxzCEbDaf1ZrcemLnWc0c= + dependencies: + es5-ext "0.8.x" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-progress@^3.4.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.9.0.tgz#25db83447deb812e62d05bac1af9aec5387ef3d4" + integrity sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA== + dependencies: + colors "^1.1.2" + string-width "^4.2.0" + +cli-spinners@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" + integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-ux@^5.2.1: + version "5.6.2" + resolved "https://registry.yarnpkg.com/cli-ux/-/cli-ux-5.6.2.tgz#c78b953b14cdf95b4bb6aae8db0ab6745333405c" + integrity sha512-CuiamOCfPaOTjbuAQXdFsfZLQmO6XSmCDxulq4y8pIets1hZ3eaysHppPKGdrcdgLugUGUap5+bXd3IukJASBA== + dependencies: + "@oclif/command" "^1.6.0" + "@oclif/errors" "^1.2.1" + "@oclif/linewrap" "^1.0.0" + "@oclif/screen" "^1.0.3" + ansi-escapes "^4.3.0" + ansi-styles "^4.2.0" + cardinal "^2.1.1" + chalk "^4.1.0" + clean-stack "^3.0.0" + cli-progress "^3.4.0" + extract-stack "^2.0.0" + fs-extra "^8.1" + hyperlinker "^1.0.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.13.1" + lodash "^4.17.11" + natural-orderby "^2.0.1" + object-treeify "^1.1.4" + password-prompt "^1.1.2" + semver "^7.3.2" + string-width "^4.2.0" + strip-ansi "^6.0.0" + supports-color "^8.1.0" + supports-hyperlinks "^2.1.0" + tslib "^2.0.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +clipboardy@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290" + integrity sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ== + dependencies: + arch "^2.1.1" + execa "^1.0.0" + is-wsl "^2.1.1" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +cmd-shim@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" + integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== + dependencies: + mkdirp-infer-owner "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.1.0.tgz#28cd9d6ac874dff97ef5ec1432c5c0b4e58e49c7" + integrity sha512-H5sDP9XDk2uP+x/xSGkgB9SEFc1bojdI5DMKU0jmSXQtml2GIe48dj1DcSS0e53QQAHn+JKqUXbGeGX24xWD7w== + +colorette@^1.2.1, colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +columnify@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + 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" + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^6.1.0, commander@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +commander@^7.0.0, commander@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.14: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression-webpack-plugin@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-7.1.2.tgz#f9a1ba84d4879693e29726f6884b382940876597" + integrity sha512-9DKNW6ILLjx+bNBoviHDgLx6swBhWWH9ApClC9sTH2NoFfQM47BapQfovCm9zjD9v1uZwInF5a925FB9ErGQeQ== + dependencies: + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + +compression@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" + integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + +concurrently@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.0.tgz#587e2cb8afca7234172d8ea55176088632c4c56d" + integrity sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.21" + read-pkg "^5.2.0" + rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + +config-chain@^1.1.12: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1" + integrity sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE= + dependencies: + dot-prop "^3.0.0" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + object-assign "^4.0.1" + os-tmpdir "^1.0.0" + osenv "^0.1.0" + uuid "^2.0.1" + write-file-atomic "^1.1.2" + xdg-basedir "^2.0.0" + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +const-ninf-float64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/const-ninf-float64/-/const-ninf-float64-1.0.0.tgz#d9e4c8d3bdd13603e98b93abcbe38f7a6db7318d" + integrity sha1-2eTI073RNgPpi5Ory+OPem23MY0= + +const-pinf-float64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/const-pinf-float64/-/const-pinf-float64-1.0.0.tgz#f6efb0d79f9c0986d3e79f2923abf9b70b63d726" + integrity sha1-9u+w15+cCYbT558pI6v5twtj1yY= + +const-smallest-float64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/const-smallest-float64/-/const-smallest-float64-1.0.0.tgz#b706684180c9d87fe0f3f946d60c747e1e54947e" + integrity sha1-twZoQYDJ2H/g8/lG1gx0fh5UlH4= + dependencies: + utils-define-read-only-property "^1.0.0" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@^1.0.4, content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +conventional-changelog-angular@^5.0.12: + version "5.0.12" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" + integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== + dependencies: + compare-func "^2.0.0" + q "^1.5.1" + +conventional-changelog-core@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz#f0897df6d53b5d63dec36b9442bd45354f8b3ce5" + integrity sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg== + dependencies: + add-stream "^1.0.0" + conventional-changelog-writer "^4.0.18" + conventional-commits-parser "^3.2.0" + dateformat "^3.0.0" + get-pkg-repo "^1.0.0" + git-raw-commits "^2.0.8" + git-remote-origin-url "^2.0.0" + git-semver-tags "^4.1.1" + lodash "^4.17.15" + normalize-package-data "^3.0.0" + q "^1.5.1" + read-pkg "^3.0.0" + read-pkg-up "^3.0.0" + shelljs "^0.8.3" + through2 "^4.0.0" + +conventional-changelog-preset-loader@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== + +conventional-changelog-writer@^4.0.18: + version "4.1.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz#1ca7880b75aa28695ad33312a1f2366f4b12659f" + integrity sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw== + dependencies: + compare-func "^2.0.0" + conventional-commits-filter "^2.0.7" + dateformat "^3.0.0" + handlebars "^4.7.6" + json-stringify-safe "^5.0.1" + lodash "^4.17.15" + meow "^8.0.0" + semver "^6.0.0" + split "^1.0.0" + through2 "^4.0.0" + +conventional-commits-filter@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" + integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== + dependencies: + lodash.ismatch "^4.4.0" + modify-values "^1.0.0" + +conventional-commits-parser@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz#ba44f0b3b6588da2ee9fd8da508ebff50d116ce2" + integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== + dependencies: + JSONStream "^1.0.4" + is-text-path "^1.0.1" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + trim-off-newlines "^1.0.0" + +conventional-recommended-bump@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" + integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== + dependencies: + concat-stream "^2.0.0" + conventional-changelog-preset-loader "^2.3.4" + conventional-commits-filter "^2.0.7" + conventional-commits-parser "^3.2.0" + git-raw-commits "^2.0.8" + git-semver-tags "^4.1.1" + meow "^8.0.0" + q "^1.5.1" + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.14.0, core-js-compat@^3.15.0: + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" + integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== + dependencies: + browserslist "^4.16.6" + semver "7.0.0" + +core-js-pure@^3.15.0: + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.2.tgz#c8e0874822705f3385d3197af9348f7c9ae2e3ce" + integrity sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +create-cert@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/create-cert/-/create-cert-1.0.6.tgz#7ed01fff7f9f0cea500aba5eff119af4c8de84f6" + integrity sha1-ftAf/3+fDOpQCrpe/xGa9MjehPY= + dependencies: + pem "^1.9.7" + pify "^3.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-error-class@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= + dependencies: + capture-stack-trace "^1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +create-test-server@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/create-test-server/-/create-test-server-3.0.1.tgz#f21140732ac99e67b50b315e46b4f98a5081ad7c" + integrity sha512-RRc+7LXSm+w3j9j3gJMVveD0Aa8uZCYhZtetpGltHZQg7ND9gW7m5jWk4OTSQgeD6PL+8gun8/CS1Pn3b5+M5A== + dependencies: + body-parser "^1.18.2" + create-cert "^1.0.2" + express "^4.15.3" + pify "^3.0.0" + +cross-fetch@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-color-names@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-1.0.1.tgz#6ff7ee81a823ad46e020fa2fd6ab40a887e2ba67" + integrity sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA== + +css-declaration-sorter@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.0.3.tgz#9dfd8ea0df4cc7846827876fafb52314890c21a9" + integrity sha512-52P95mvW1SMzuRZegvpluT6yEv0FqQusydKQPZsNN5Q7hh8EwQvN8E2nwuJ16BBvNN6LcoIZXu/Bk58DAhrrxw== + dependencies: + timsort "^0.3.0" + +css-loader@^5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1" + integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w== + dependencies: + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" + +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.3.tgz#caa54183a8c8df03124a9e23f374ab89df5a9a99" + integrity sha512-qo9tX+t4yAAZ/yagVV3b+QBKeLklQbmgR3wI7mccrDcR+bEk9iHgZN1E7doX68y9ThznLya3RDmR+nc7l6/2WQ== + dependencies: + css-declaration-sorter "^6.0.3" + cssnano-utils "^2.0.1" + postcss-calc "^8.0.0" + postcss-colormin "^5.2.0" + postcss-convert-values "^5.0.1" + postcss-discard-comments "^5.0.1" + postcss-discard-duplicates "^5.0.1" + postcss-discard-empty "^5.0.1" + postcss-discard-overridden "^5.0.1" + postcss-merge-longhand "^5.0.2" + postcss-merge-rules "^5.0.2" + postcss-minify-font-values "^5.0.1" + postcss-minify-gradients "^5.0.1" + postcss-minify-params "^5.0.1" + postcss-minify-selectors "^5.1.0" + postcss-normalize-charset "^5.0.1" + postcss-normalize-display-values "^5.0.1" + postcss-normalize-positions "^5.0.1" + postcss-normalize-repeat-style "^5.0.1" + postcss-normalize-string "^5.0.1" + postcss-normalize-timing-functions "^5.0.1" + postcss-normalize-unicode "^5.0.1" + postcss-normalize-url "^5.0.2" + postcss-normalize-whitespace "^5.0.1" + postcss-ordered-values "^5.0.2" + postcss-reduce-initial "^5.0.1" + postcss-reduce-transforms "^5.0.1" + postcss-svgo "^5.0.2" + postcss-unique-selectors "^5.0.1" + +cssnano-utils@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2" + integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ== + +cssnano@^5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.6.tgz#2a91ad34c6521ae31eab3da9c90108ea3093535d" + integrity sha512-NiaLH/7yqGksFGsFNvSRe2IV/qmEBAeDE64dYeD8OBrgp6lE8YoMeQJMtsv5ijo6MPyhuoOvFhI94reahBRDkw== + dependencies: + cosmiconfig "^7.0.0" + cssnano-preset-default "^5.1.3" + is-resolvable "^1.1.0" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +date-fns@^2.16.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" + integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== + +dateformat@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== + +dayjs@^1.10.3, dayjs@^1.10.4: + version "1.10.5" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986" + integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g== + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decamelize@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-3.2.0.tgz#84b8e8f4f8c579f938e35e2cc7024907e0090851" + integrity sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw== + dependencies: + xregexp "^4.2.4" + +decimal.js@^10.2.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@^1.1.2, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +detect-indent@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +dezalgo@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= + dependencies: + asap "^2.0.0" + wrappy "1" + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +difflib@~0.2.1: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha1-teMDYabbAjF21WKJLbhZQKcY9H4= + dependencies: + heap ">= 0.2.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" + integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" + integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc= + dependencies: + is-obj "^1.0.0" + +dot-prop@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + +dreamopt@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/dreamopt/-/dreamopt-0.6.0.tgz#d813ccdac8d39d8ad526775514a13dda664d6b4b" + integrity sha1-2BPM2sjTnYrVJndVFKE92mZNa0s= + dependencies: + wordwrap ">=0.0.2" + +dset@^2.0.1, dset@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dset/-/dset-2.1.0.tgz#cd1e99e55cf32366d8f144f906c42f7fb3bf431e" + integrity sha512-hlQYwNEdW7Qf8zxysy+yN1E8C/SxRst3Z9n+IvXOR35D9bPVwNHhnL8ZBeoZjvinuGrlvGg6pAMDwhmjqFDgjA== + +duplexer2@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@^0.1.1, duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.723: + version "1.3.762" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.762.tgz#3fa4e3bcbda539b50e3aa23041627063a5cffe61" + integrity sha512-LehWjRpfPcK8F1Lf/NZoAwWLWnjJVo0SZeQ9j/tvnBWYcT99qDqgo4raAfS2oTKZjPrR/jxruh85DGgDUmywEA== + +elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + +"emoji-regex@>=6.0.0 <=6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" + integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^5.8.0: + version "5.8.2" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" + integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@^2.3.5, enquirer@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +envinfo@^7.7.3, envinfo@^7.7.4: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.6.0.tgz#e72ab05b7412e62b9be37c37a09bdb6000d706f0" + integrity sha512-f8kcHX1ArhllUtb/wVSyvygoKCznIjnxhLxy7TCvIiMdT7fL4ZDTIKaadMe6eLvOXg6Wk02UeoFgUoZ2EKZZUA== + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@0.8.x: + version "0.8.2" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.8.2.tgz#aba8d9e1943a895ac96837a62a39b3f55ecd94ab" + integrity sha1-q6jZ4ZQ6iVrJaDemKjmz9V7NlKs= + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.51, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promisify@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621" + integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-regexp-component@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz#9c63b6d0b25ff2a88c3adbd18c5b61acc3b9faa2" + integrity sha1-nGO20LJf8qiMOtvRjFthrMO5+qI= + +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-prettier@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" + integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== + dependencies: + get-stdin "^6.0.0" + +eslint-config-xo-typescript@^0.35.0: + version "0.35.0" + resolved "https://registry.yarnpkg.com/eslint-config-xo-typescript/-/eslint-config-xo-typescript-0.35.0.tgz#4cd8c1208e0f96b80ec8d39dd61930e978e95b59" + integrity sha512-Pgj9mX0T74Ro8FCvVO1ZjWWcPDW6J42SByG4GMRwMXXZrSwOUY83vj4Ii/+Q+amg2XJwyx0o5/sCprUP/nGM0Q== + +eslint-plugin-jest@^24.3.6: + version "24.3.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz#5f0ca019183c3188c5ad3af8e80b41de6c8e9173" + integrity sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg== + dependencies: + "@typescript-eslint/experimental-utils" "^4.0.1" + +eslint-plugin-prettier@^3.1.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" + integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@5.1.1, eslint-scope@^5.0.0, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint@^7.25.0: + version "7.29.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.29.0.tgz#ee2a7648f2e729485e4d0bd6383ec1deabc8b3c0" + integrity sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0, esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.0.0, events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0, execa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +express@^4.15.3: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-stack@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" + integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fancy-test@^1.4.3: + version "1.4.10" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-1.4.10.tgz#310be93d4aa45d788bce56a573ae4d1b92b2e1a0" + integrity sha512-AaUX6wKS7D5OP2YK2q5G7c8PGx2lgoyLUD7Bbg8z323sb9aebBqzb9UN6phzI73UgO/ViihmNfOxF3kdfZLhew== + dependencies: + "@types/chai" "*" + "@types/lodash" "*" + "@types/node" "*" + "@types/sinon" "*" + lodash "^4.17.13" + mock-stdin "^1.0.0" + nock "^13.0.0" + stdout-stderr "^0.1.9" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.0.3, fast-glob@^3.1.1: + version "3.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.6.tgz#434dd9529845176ea049acc9343e8282765c6e1a" + integrity sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + dependencies: + punycode "^1.3.2" + +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + +fastq@^1.6.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +figgy-pudding@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +filled-array@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84" + integrity sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q= + +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-versions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965" + integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ== + dependencies: + semver-regex "^3.1.2" + +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + +flow-parser@0.*: + version "0.154.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.154.0.tgz#ea195e3c9f9a6488466e5e29e80b50cea8035485" + integrity sha512-cH9xY/ljOgmqG1n7PU1jffiHhRggoloauwOrOlCWBEX4Y+ml6GA8g//tCVKU+6PO4BXoPF22TFHkS5E1bN3JOQ== + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + integrity sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.0.1, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-minipass@^2.0.0, fs-minipass@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@^2.1.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-pkg-repo@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d" + integrity sha1-xztInAbYDMVTbCyFP54FIyBWly0= + dependencies: + hosted-git-info "^2.1.4" + meow "^3.3.0" + normalize-package-data "^2.3.0" + parse-github-repo-url "^1.3.0" + through2 "^2.0.0" + +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +git-raw-commits@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" + integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + +git-remote-origin-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= + dependencies: + gitconfiglocal "^1.0.0" + pify "^2.3.0" + +git-semver-tags@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" + integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== + dependencies: + meow "^8.0.0" + semver "^6.0.0" + +git-up@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.2.tgz#10c3d731051b366dc19d3df454bfca3f77913a7c" + integrity sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ== + dependencies: + is-ssh "^1.3.0" + parse-url "^5.0.0" + +git-url-parse@^11.4.4: + version "11.5.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.5.0.tgz#acaaf65239cb1536185b19165a24bbc754b3f764" + integrity sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA== + dependencies: + git-up "^4.0.0" + +gitconfiglocal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= + dependencies: + ini "^1.3.2" + +github-slugger@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" + integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== + dependencies: + emoji-regex ">=6.0.0 <=6.1.1" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-promise@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" + integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== + dependencies: + "@types/glob" "*" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.6.0, globals@^13.9.0: + version "13.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" + integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== + dependencies: + type-fest "^0.20.2" + +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +globby@^11.0.1, globby@^11.0.2, globby@^11.0.3: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +got@^5.0.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" + integrity sha1-X4FjWmHkplifGAVp6k44FoClHzU= + dependencies: + create-error-class "^3.0.1" + duplexer2 "^0.1.4" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + node-status-codes "^1.0.0" + object-assign "^4.0.1" + parse-json "^2.1.0" + pinkie-promise "^2.0.0" + read-all-stream "^3.0.0" + readable-stream "^2.0.5" + timed-out "^3.0.0" + unzip-response "^1.0.2" + url-parse-lax "^1.0.0" + +got@^9.0.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handlebars@^4.7.6: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-unicode@^2.0.0, has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +"heap@>= 0.2.0": + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + dependencies: + lru-cache "^6.0.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-call@^5.1.2: + version "5.3.0" + resolved "https://registry.yarnpkg.com/http-call/-/http-call-5.3.0.tgz#4ded815b13f423de176eb0942d69c43b25b148db" + integrity sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w== + dependencies: + content-type "^1.0.4" + debug "^4.1.1" + is-retry-allowed "^1.1.0" + is-stream "^2.0.0" + parse-json "^4.0.0" + tunnel-agent "^0.6.0" + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + +husky@^4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d" + integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow== + dependencies: + chalk "^4.0.0" + ci-info "^2.0.0" + compare-versions "^3.6.0" + cosmiconfig "^7.0.0" + find-versions "^4.0.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^5.0.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + +hyperlinker@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" + integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ieee754@^1.1.13, ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore-walk@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== + dependencies: + minimatch "^3.0.4" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.1, ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.3, infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +init-package-json@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" + integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== + dependencies: + glob "^7.1.1" + npm-package-arg "^8.1.2" + promzard "^0.3.0" + read "~1.0.1" + read-package-json "^3.0.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + validate-npm-package-name "^3.0.0" + +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + dependencies: + call-bind "^1.0.2" + +is-buffer@^1.1.5, is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-color-stop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= + +is-number-object@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.0, is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +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-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= + +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-resolvable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0, is-retry-allowed@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-ssh@^1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== + dependencies: + protocols "^1.1.0" + +is-stream@^1.0.0, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= + dependencies: + text-extensions "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-wsl@^2.1.1, is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" + prompts "^2.0.1" + yargs "^15.4.1" + +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.0.0, jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.6.2" + is-generator-fn "^2.0.0" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.1.0, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== + dependencies: + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.6.2" + +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.0.2: + version "27.0.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" + integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@26.6.3, jest@^26.0.1, jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== + dependencies: + "@jest/core" "^26.6.3" + import-local "^3.0.2" + jest-cli "^26.6.3" + +joi@^17.4.0: + version "17.4.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" + integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jscodeshift-add-imports@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/jscodeshift-add-imports/-/jscodeshift-add-imports-1.0.10.tgz#3d0f99d539d492cfa037aa4a63f04c4a626bcff5" + integrity sha512-VUe9DJ3zkWIR62zSRQnmsOVeyt77yD8knvYNna/PzRZlF9j799hJw5sqTZu4EX16XLIqS3FxWz3nXuGuiw9iyQ== + dependencies: + "@babel/traverse" "^7.4.5" + jscodeshift-find-imports "^2.0.2" + +jscodeshift-find-imports@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/jscodeshift-find-imports/-/jscodeshift-find-imports-2.0.4.tgz#4dc427bff6c8f8c6c766a19043cdbee4e1d10782" + integrity sha512-HxOzjWDOFFSCf8EKSTQGqCxXeRFqZszOywnZ0HuMB9YPDFHVpxftGRsY+QS+Qq8o2qUojlmNU3JEHts5DWYS1A== + +jscodeshift@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.12.0.tgz#de7302f3d3e1f4b3b36f9e484f451ba4ab7cc1f4" + integrity sha512-LEgr+wklbtEQD6SptyVYox+YZ7v+4NQeWHgqASedxl2LxQ+/kSQs6Nhs/GX+ymVOu84Hsz9/C2hQfDY89dKZ6A== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" + babel-core "^7.0.0-bridge.0" + colors "^1.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^3.1.10" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.20.4" + temp "^0.8.1" + write-file-atomic "^2.3.0" + +jsdom@^16.4.0, jsdom@^16.5.3: + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.5" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-diff@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-diff/-/json-diff-0.5.4.tgz#7bc8198c441756632aab66c7d9189d365a7a035a" + integrity sha512-q5Xmx9QXNOzOzIlMoYtLrLiu4Jl/Ce2bn0CNcv54PhyH89CI4GWlGVDye8ei2Ijt9R3U+vsWPsXpLUNob8bs8Q== + dependencies: + cli-color "~0.1.6" + difflib "~0.2.1" + dreamopt "~0.6.0" + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-ref-parser@^9.0.6: + version "9.0.9" + resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#66ea538e7450b12af342fa3d5b8458bc1e1e013f" + integrity sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q== + dependencies: + "@apidevtools/json-schema-ref-parser" "9.0.9" + +json-schema-to-typescript@^10.1.4: + version "10.1.4" + resolved "https://registry.yarnpkg.com/json-schema-to-typescript/-/json-schema-to-typescript-10.1.4.tgz#6d8ae66937d07bd054095fc4a541aa81acea3549" + integrity sha512-HWm23Z6Fnj3rnm4FKZh3sMz70hNoA+AfqVuV+ZcwFIhq6v76YVUMZSLlonrw7GI5yIoiuuJjB5rqakIYRCLlsg== + dependencies: + "@types/json-schema" "^7.0.6" + "@types/lodash" "^4.14.168" + "@types/prettier" "^2.1.5" + cli-color "^2.0.0" + get-stdin "^8.0.0" + glob "^7.1.6" + glob-promise "^3.4.0" + is-glob "^4.0.1" + json-schema-ref-parser "^9.0.6" + json-stringify-safe "^5.0.1" + lodash "^4.17.20" + minimist "^1.2.5" + mkdirp "^1.0.4" + mz "^2.7.0" + prettier "^2.2.0" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json2csv@^5.0.4: + version "5.0.6" + resolved "https://registry.yarnpkg.com/json2csv/-/json2csv-5.0.6.tgz#590e0e1b9579e59baa53bda0c0d840f4d8009687" + integrity sha512-0/4Lv6IenJV0qj2oBdgPIAmFiKKnh8qh7bmLFJ+/ZZHLjSeiL3fKKGX3UryvKPbxFbhV+JcYo9KUC19GJ/Z/4A== + dependencies: + commander "^6.1.0" + jsonparse "^1.3.1" + lodash.get "^4.4.2" + +json5@2.x, json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0, jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +kleur@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" + integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== + +klona@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + +latest-version@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b" + integrity sha1-VvjWE5YghHuAF/jx9NeOIRMkFos= + dependencies: + package-json "^2.0.0" + +lerna@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" + integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== + dependencies: + "@lerna/add" "4.0.0" + "@lerna/bootstrap" "4.0.0" + "@lerna/changed" "4.0.0" + "@lerna/clean" "4.0.0" + "@lerna/cli" "4.0.0" + "@lerna/create" "4.0.0" + "@lerna/diff" "4.0.0" + "@lerna/exec" "4.0.0" + "@lerna/import" "4.0.0" + "@lerna/info" "4.0.0" + "@lerna/init" "4.0.0" + "@lerna/link" "4.0.0" + "@lerna/list" "4.0.0" + "@lerna/publish" "4.0.0" + "@lerna/run" "4.0.0" + "@lerna/version" "4.0.0" + import-local "^3.0.2" + npmlog "^4.1.2" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +libnpmaccess@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== + dependencies: + aproba "^2.0.0" + minipass "^3.1.1" + npm-package-arg "^8.1.2" + npm-registry-fetch "^11.0.0" + +libnpmpublish@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== + dependencies: + normalize-package-data "^3.0.2" + npm-package-arg "^8.1.2" + npm-registry-fetch "^11.0.0" + semver "^7.1.3" + ssri "^8.0.1" + +lilconfig@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" + integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +lint-staged@^10.5.3: + version "10.5.4" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665" + integrity sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg== + dependencies: + chalk "^4.1.0" + cli-truncate "^2.1.0" + commander "^6.2.0" + cosmiconfig "^7.0.0" + debug "^4.2.0" + dedent "^0.7.0" + enquirer "^2.3.6" + execa "^4.1.0" + listr2 "^3.2.2" + log-symbols "^4.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "^3.3.0" + +listr2@^3.2.2: + version "3.10.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.10.0.tgz#58105a53ed7fa1430d1b738c6055ef7bb006160f" + integrity sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw== + dependencies: + cli-truncate "^2.1.0" + colorette "^1.2.2" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.7" + through "^2.3.8" + wrap-ansi "^7.0.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +load-json-file@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" + integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== + dependencies: + graceful-fs "^4.1.15" + parse-json "^5.0.0" + strip-bom "^4.0.0" + type-fest "^0.6.0" + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@^1.2.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash.ismatch@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + +lodash.template@^4.4.0, lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@4.x, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.0.0, log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +make-fetch-happen@^8.0.9: + version "8.0.14" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.0.5" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + +make-fetch-happen@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.3.tgz#57bbfb5b859807cd28005ca85aa6a72568675e24" + integrity sha512-uZ/9Cf2vKqsSWZyXhZ9wHHyckBrkntgbnqV68Bfe8zZenlf7D6yuGMXvHZQ+jSnzPkjosuNP1HGasj1J4h8OlQ== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-obj@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" + integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +math-abs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/math-abs/-/math-abs-1.0.2.tgz#8fb2675d969327a61a629821fc23e41faac5c4d3" + integrity sha1-j7JnXZaTJ6YaYpgh/CPkH6rFxNM= + +math-float64-copysign@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-copysign/-/math-float64-copysign-1.0.0.tgz#8aec85d28d81c7a23cab0748a23c5f01916966f6" + integrity sha1-iuyF0o2Bx6I8qwdIojxfAZFpZvY= + dependencies: + math-float64-from-words "^1.0.0" + math-float64-get-high-word "^1.0.0" + math-float64-to-words "^1.0.0" + +math-float64-exponent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-exponent/-/math-float64-exponent-1.0.0.tgz#d8893e835f5e65812add6ce7abafee93daa75ca5" + integrity sha1-2Ik+g19eZYEq3Wznq6/uk9qnXKU= + dependencies: + math-float64-get-high-word "^1.0.0" + +math-float64-from-words@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-from-words/-/math-float64-from-words-1.0.0.tgz#94648f9377f8128a8add54856cba951f6e90a139" + integrity sha1-lGSPk3f4EoqK3VSFbLqVH26QoTk= + dependencies: + utils-is-little-endian "^1.0.0" + +math-float64-get-high-word@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-get-high-word/-/math-float64-get-high-word-1.0.0.tgz#9fb7107e366585d5bb0efddc036f947c9666611a" + integrity sha1-n7cQfjZlhdW7Dv3cA2+UfJZmYRo= + dependencies: + utils-is-little-endian "^1.0.0" + +math-float64-ldexp@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/math-float64-ldexp/-/math-float64-ldexp-1.0.1.tgz#e8076daa29a6529a572002fa9fd9e3e3c52253da" + integrity sha1-6AdtqimmUppXIAL6n9nj48UiU9o= + dependencies: + const-ninf-float64 "^1.0.0" + const-pinf-float64 "^1.0.0" + math-float64-copysign "^1.0.0" + math-float64-exponent "^1.0.0" + math-float64-from-words "^1.0.0" + math-float64-normalize "^1.0.0" + math-float64-to-words "^1.0.0" + +math-float64-normalize@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-normalize/-/math-float64-normalize-1.0.0.tgz#d8bba30804df177bf1d76f641315301fa1260077" + integrity sha1-2LujCATfF3vx129kExUwH6EmAHc= + dependencies: + const-smallest-float64 "^1.0.0" + math-abs "^1.0.2" + validate.io-infinite "^1.0.0" + +math-float64-to-words@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/math-float64-to-words/-/math-float64-to-words-1.0.0.tgz#624ebd00c7ff87b2bbcba6e230f904e8a83e061e" + integrity sha1-Yk69AMf/h7K7y6biMPkE6Kg+Bh4= + dependencies: + utils-is-little-endian "^1.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +md5@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memoizee@^0.4.14: + version "0.4.15" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + 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" + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +meow@^8.0.0: + version "8.1.2" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.48.0, "mime-db@>= 1.43.0 < 2": + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.3.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" + integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +minizlib@^2.0.0, minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp-infer-owner@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" + integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== + dependencies: + chownr "^2.0.0" + infer-owner "^1.0.4" + mkdirp "^1.0.3" + +mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-stdin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" + integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== + +modify-values@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multimatch@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +mute-stream@0.0.8, mute-stream@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nan@^2.12.1: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nanoid@^2.1.7: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +natural-orderby@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" + integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== + +negotiator@0.6.2, negotiator@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +new-date@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/new-date/-/new-date-1.0.3.tgz#a5956086d3f5ed43d0b210d87a10219ccb7a2326" + integrity sha512-0fsVvQPbo2I18DT2zVHpezmeeNYV2JaJSrseiHLc17GNOxJzUdx5mvSigPu8LtIfZSij5i1wXnXFspEs2CD6hA== + dependencies: + "@segment/isodate" "1.0.3" + +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +nock@^13.0.0, nock@^13.0.11: + version "13.1.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.1.0.tgz#41c8ce8b35ab7d618c4cbf40de1d5bce319979ba" + integrity sha512-3N3DUY8XYrxxzWazQ+nSBpiaJ3q6gcpNh4gXovC/QBxrsvNp4tq+wsLHF6mJ3nrn3lPLn7KCJqKxy/9aD+0fdw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + +node-cache@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + +node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + +node-fetch@2.6.1, node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-gyp@^5.0.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" + integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.1.2" + request "^2.88.0" + rimraf "^2.6.3" + semver "^5.7.1" + tar "^4.4.12" + which "^1.3.1" + +node-gyp@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +node-releases@^1.1.71: + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== + +node-status-codes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" + integrity sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8= + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" + integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== + dependencies: + hosted-git-info "^4.0.1" + resolve "^1.20.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@4.5.1, normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-bundled@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-install-checks@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== + dependencies: + semver "^7.1.1" + +npm-lifecycle@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== + dependencies: + byline "^5.0.0" + graceful-fs "^4.1.15" + node-gyp "^5.0.2" + resolve-from "^4.0.0" + slide "^1.1.6" + uid-number "0.0.6" + umask "^1.1.0" + which "^1.3.1" + +npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: + version "8.1.5" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== + dependencies: + hosted-git-info "^4.0.1" + semver "^7.3.4" + validate-npm-package-name "^3.0.0" + +npm-packlist@^2.1.4: + version "2.2.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== + dependencies: + npm-install-checks "^4.0.0" + npm-normalize-package-bin "^1.0.1" + npm-package-arg "^8.1.2" + semver "^7.3.4" + +npm-registry-fetch@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== + dependencies: + make-fetch-happen "^9.0.1" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + +npm-registry-fetch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== + dependencies: + "@npmcli/ci-detect" "^1.0.0" + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0, npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +obj-case@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/obj-case/-/obj-case-0.2.1.tgz#13a554d04e5ca32dfd9d566451fd2b0e11007f1a" + integrity sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg== + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.10.3, object-inspect@^1.9.0: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-treeify@^1.1.4: + version "1.1.33" + resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" + integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" + integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +optimize-css-assets-webpack-plugin@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-6.0.1.tgz#7719bceabba1f3891ec3ae04efb81a1cc99cd793" + integrity sha512-BshV2UZPfggZLdUfN3zFBbG4sl/DynUI+YCB6fRRDWaqO2OiWN8GPcp4Y0/fEV6B3k9Hzyk3czve3V/8B/SzKQ== + dependencies: + cssnano "^5.0.2" + last-call-webpack-plugin "^3.0.0" + postcss "^8.2.1" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + 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" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +ora@^5.4.0, ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.0, osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-pipe@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" + integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-reduce@^2.0.0, p-reduce@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +p-waterfall@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" + integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== + dependencies: + p-reduce "^2.0.0" + +package-json@^2.0.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb" + integrity sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs= + dependencies: + got "^5.0.0" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +pacote@^11.2.6: + version "11.3.4" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.4.tgz#c290b790a5cee3082bb8fa223f3f3e2fdf3d0bfc" + integrity sha512-RfahPCunM9GI7ryJV/zY0bWQiokZyLqaSNHXtbNSoLb7bwTvBbJBEyCJ01KWs4j1Gj7GmX8crYXQ1sNX6P2VKA== + dependencies: + "@npmcli/git" "^2.0.1" + "@npmcli/installed-package-contents" "^1.0.6" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.8.2" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^11.0.0" + promise-retry "^2.0.1" + read-package-json-fast "^2.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.1.0" + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-github-repo-url@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" + integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= + +parse-json@^2.1.0, parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-path@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== + dependencies: + is-ssh "^1.3.0" + protocols "^1.4.0" + qs "^6.9.4" + query-string "^6.13.8" + +parse-url@^5.0.0: + version "5.0.7" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.7.tgz#2ca3c32816f1a092c35e1f2afe63bb7924dde261" + integrity sha512-CgbjyWT6aOh2oNSUS0cioYQsGysj9hQ2IdbOfeNwq5KOaKM7dOw/yTupiI0cnJhaDHJEIGybPkQz7LF9WNIhyw== + dependencies: + is-ssh "^1.3.0" + normalize-url "4.5.1" + parse-path "^4.0.0" + protocols "^1.4.0" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +password-prompt@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.2.tgz#85b2f93896c5bd9e9f2d6ff0627fa5af3dc00923" + integrity sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA== + dependencies: + ansi-escapes "^3.1.0" + cross-spawn "^6.0.5" + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-to-regexp@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" + integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pem@^1.9.7: + version "1.14.4" + resolved "https://registry.yarnpkg.com/pem/-/pem-1.14.4.tgz#a68c70c6e751ccc5b3b5bcd7af78b0aec1177ff9" + integrity sha512-v8lH3NpirgiEmbOqhx0vwQTxwi0ExsiWBGYh0jYNq7K6mQuO4gI6UEFlr6fLAdv9TPXRt6GqiwE37puQdIDS8g== + dependencies: + es6-promisify "^6.0.0" + md5 "^2.2.1" + os-tmpdir "^1.0.1" + which "^2.0.2" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.0, pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +pkginfo@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" + integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= + +platform@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + +pnp-webpack-plugin@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.0.0.tgz#a05b87aacd132740a5db09462a3612453e5df90a" + integrity sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g== + dependencies: + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-colormin@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.0.tgz#2b620b88c0ff19683f3349f4cf9e24ebdafb2c88" + integrity sha512-+HC6GfWU3upe5/mqmxuqYZ9B2Wl4lcoUUNkoaX59nEWV4EtADCMiBqui111Bu8R8IvaZTmqmxrqOAqjbHIwXPw== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-convert-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz#4ec19d6016534e30e3102fdf414e753398645232" + integrity sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-discard-comments@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz#9eae4b747cf760d31f2447c27f0619d5718901fe" + integrity sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg== + +postcss-discard-duplicates@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz#68f7cc6458fe6bab2e46c9f55ae52869f680e66d" + integrity sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA== + +postcss-discard-empty@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz#ee136c39e27d5d2ed4da0ee5ed02bc8a9f8bf6d8" + integrity sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw== + +postcss-discard-overridden@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz#454b41f707300b98109a75005ca4ab0ff2743ac6" + integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q== + +postcss-merge-longhand@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz#277ada51d9a7958e8ef8cf263103c9384b322a41" + integrity sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw== + dependencies: + css-color-names "^1.0.1" + postcss-value-parser "^4.1.0" + stylehacks "^5.0.1" + +postcss-merge-rules@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz#d6e4d65018badbdb7dcc789c4f39b941305d410a" + integrity sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^2.0.1" + postcss-selector-parser "^6.0.5" + vendors "^1.0.3" + +postcss-minify-font-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz#a90cefbfdaa075bd3dbaa1b33588bb4dc268addf" + integrity sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-minify-gradients@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz#2dc79fd1a1afcb72a9e727bc549ce860f93565d2" + integrity sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g== + dependencies: + cssnano-utils "^2.0.1" + is-color-stop "^1.1.0" + postcss-value-parser "^4.1.0" + +postcss-minify-params@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz#371153ba164b9d8562842fdcd929c98abd9e5b6c" + integrity sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw== + dependencies: + alphanum-sort "^1.0.2" + browserslist "^4.16.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz#4385c845d3979ff160291774523ffa54eafd5a54" + integrity sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og== + dependencies: + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-normalize-charset@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz#121559d1bebc55ac8d24af37f67bd4da9efd91d0" + integrity sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg== + +postcss-normalize-display-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz#62650b965981a955dffee83363453db82f6ad1fd" + integrity sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ== + dependencies: + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-normalize-positions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz#868f6af1795fdfa86fbbe960dceb47e5f9492fe5" + integrity sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-normalize-repeat-style@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz#cbc0de1383b57f5bb61ddd6a84653b5e8665b2b5" + integrity sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w== + dependencies: + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-normalize-string@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz#d9eafaa4df78c7a3b973ae346ef0e47c554985b0" + integrity sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-normalize-timing-functions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz#8ee41103b9130429c6cbba736932b75c5e2cb08c" + integrity sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q== + dependencies: + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-normalize-unicode@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz#82d672d648a411814aa5bf3ae565379ccd9f5e37" + integrity sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA== + dependencies: + browserslist "^4.16.0" + postcss-value-parser "^4.1.0" + +postcss-normalize-url@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz#ddcdfb7cede1270740cf3e4dfc6008bd96abc763" + integrity sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ== + dependencies: + is-absolute-url "^3.0.3" + normalize-url "^6.0.1" + postcss-value-parser "^4.1.0" + +postcss-normalize-whitespace@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz#b0b40b5bcac83585ff07ead2daf2dcfbeeef8e9a" + integrity sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA== + dependencies: + postcss-value-parser "^4.1.0" + +postcss-ordered-values@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz#1f351426977be00e0f765b3164ad753dac8ed044" + integrity sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ== + dependencies: + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-reduce-initial@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz#9d6369865b0f6f6f6b165a0ef5dc1a4856c7e946" + integrity sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw== + dependencies: + browserslist "^4.16.0" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz#93c12f6a159474aa711d5269923e2383cedcf640" + integrity sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA== + dependencies: + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.2.tgz#bc73c4ea4c5a80fbd4b45e29042c34ceffb9257f" + integrity sha512-YzQuFLZu3U3aheizD+B1joQ94vzPfE6BNUcSYuceNxlVnKKsOtdo6hL9/zyC168Q8EwfLSgaDSalsUGa9f2C0A== + dependencies: + postcss-value-parser "^4.1.0" + svgo "^2.3.0" + +postcss-unique-selectors@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz#3be5c1d7363352eff838bd62b0b07a0abad43bfc" + integrity sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w== + dependencies: + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" + uniqs "^2.0.0" + +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^8.2.1, postcss@^8.2.15: + version "8.3.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" + integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map-js "^0.6.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.0.5, prettier@^2.1.2, prettier@^2.2.0, prettier@^2.2.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" + integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== + +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +prompts@^2.0.1, prompts@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +promzard@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= + dependencies: + read "1" + +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +protocols@^1.1.0, protocols@^1.4.0: + version "1.4.8" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28, psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4, punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qqjs@^0.3.10: + version "0.3.11" + resolved "https://registry.yarnpkg.com/qqjs/-/qqjs-0.3.11.tgz#795b9f7d00807d75c391b1241b5be3077143d9ea" + integrity sha512-pB2X5AduTl78J+xRSxQiEmga1jQV0j43jOPs/MTgTLApGFEOn6NgdE2dEjp7nvDtjkIOZbvFIojAiYUx6ep3zg== + dependencies: + chalk "^2.4.1" + debug "^4.1.1" + execa "^0.10.0" + fs-extra "^6.0.1" + get-stream "^5.1.0" + glob "^7.1.2" + globby "^10.0.1" + http-call "^5.1.2" + load-json-file "^6.2.0" + pkg-dir "^4.2.0" + tar-fs "^2.0.0" + tmp "^0.1.0" + write-json-file "^4.1.1" + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^6.9.4: + version "6.10.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^6.10.1, query-string@^6.13.8: + version "6.14.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.0.1, rc@^1.1.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +read-all-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" + integrity sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po= + dependencies: + pinkie-promise "^2.0.0" + readable-stream "^2.0.0" + +read-cmd-shim@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" + integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== + +read-package-json-fast@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz#2dcb24d9e8dd50fb322042c8c35a954e6cc7ac9e" + integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + +read-package-json@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^2.0.0" + npm-normalize-package-bin "^1.0.0" + +read-package-json@^3.0.0, read-package-json@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" + integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^3.0.0" + npm-normalize-package-bin "^1.0.0" + +read-package-tree@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== + dependencies: + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + util-promisify "^2.1.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +read@1, read@~1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdir-scoped-modules@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +recast@^0.20.3, recast@^0.20.4: + version "0.20.4" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.4.tgz#db55983eac70c46b3fff96c8e467d65ffb4a7abc" + integrity sha512-6qLIBGGRcwjrTZGIiBpJVC/NeuXpogXNyRQpqU1zWPUigCphvApoCs9KIwDYh1eDuJ6dAFlQoi/QUyE5KQ6RBQ== + dependencies: + ast-types "0.14.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +rechoir@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" + integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q== + dependencies: + resolve "^1.9.0" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +redeyed@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + dependencies: + esprima "~4.0.0" + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-auth-token@^3.0.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" + integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@3.1.0, registry-url@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= + dependencies: + rc "^1.0.1" + +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + +regjsparser@^0.6.4: + version "0.6.9" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" + integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request@^2.88.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.2: + version "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== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.9.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rxjs@^6.6.0, rxjs@^6.6.3, rxjs@^6.6.7: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= + dependencies: + semver "^5.0.3" + +semver-regex@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807" + integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA== + +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-handler@6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8" + integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.0.4" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +serve@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/serve/-/serve-12.0.0.tgz#122962f712b57660059de9d109c82599280e4949" + integrity sha512-BkTsETQYynAZ7rXX414kg4X6EvuZQS3UVs1NY0VQYdRHSTYWPYcH38nnDh48D0x6ONuislgjag8uKlU2gTBImA== + dependencies: + "@zeit/schemas" "2.6.0" + ajv "6.12.6" + arg "2.0.0" + boxen "1.3.0" + chalk "2.4.1" + clipboardy "2.3.0" + compression "1.7.3" + serve-handler "6.1.3" + update-check "1.5.2" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shelljs@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +sirv@^1.0.7: + version "1.0.12" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.12.tgz#d816c882b35489b3c63290e2f455ae3eccd5f652" + integrity sha512-+jQoCxndz7L2tqQL4ZyzfDhky0W/4ZJip3XoOuxyQWnAwMxindLl3Xv1qT4x1YX/re0leShvTm8Uk0kQspGhBg== + dependencies: + "@polka/url" "^1.0.0-next.15" + mime "^2.3.1" + totalist "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +size-limit@^4.10.1: + version "4.12.0" + resolved "https://registry.yarnpkg.com/size-limit/-/size-limit-4.12.0.tgz#ecc9c0448c049a40b10e76b5e1b4a20f99a54468" + integrity sha512-LwlUDPxFJbJDIJsBE5bKo8kFMuxmuewBMDjgfSoQwnO27V8DSK+j6881nsrX3GoM3bJMFIeEq56thqBEdYC8bw== + dependencies: + bytes-iec "^3.1.1" + chokidar "^3.5.1" + ci-job-number "^1.2.2" + colorette "^1.2.2" + globby "^11.0.3" + lilconfig "^2.0.3" + ora "^5.4.1" + read-pkg-up "^7.0.1" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slide@^1.1.5, slide@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= + +slugify@^1.5.0: + version "1.5.3" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.5.3.tgz#36e009864f5476bfd5db681222643d92339c890d" + integrity sha512-/HkjRdwPY3yHJReXu38NiusZw2+LLE2SrhkWJtmlPDB1fqFSvioYj62NkPcrKiNCgRLeGcGK7QBvr1iQwybeXw== + +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +socks-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== + dependencies: + agent-base "^6.0.2" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.6.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +sort-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" + integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== + dependencies: + is-plain-obj "^2.0.0" + +source-list-map@^2.0.0, source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3, source-map@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spark-md5@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" + integrity sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig== + +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" + integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split2@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== + dependencies: + figgy-pudding "^3.5.1" + +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stats-median@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stats-median/-/stats-median-1.0.1.tgz#ca8497cb1014d23d145db4d6fc93c8e815eed3ef" + integrity sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q== + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stdout-stderr@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/stdout-stderr/-/stdout-stderr-0.1.13.tgz#54e3450f3d4c54086a49c0c7f8786a44d1844b6f" + integrity sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA== + dependencies: + debug "^4.1.1" + strip-ansi "^6.0.0" + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +strong-log-transformer@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== + dependencies: + duplexer "^0.1.1" + minimist "^1.2.0" + through "^2.3.4" + +style-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +stylehacks@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb" + integrity sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA== + dependencies: + browserslist "^4.16.0" + postcss-selector-parser "^6.0.4" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0, supports-color@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +svgo@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.3.1.tgz#603a69ce50311c0e36791528f549644ec1b3f4bc" + integrity sha512-riDDIQgXpEnn0BEl9Gvhh1LNLIyiusSpt64IR8upJu7MwxnzetmF/Y57pXQD2NMX2lVyMRzXt5f2M5rO4wG7Dw== + dependencies: + "@trysound/sax" "0.1.1" + chalk "^4.1.0" + commander "^7.1.0" + css-select "^4.1.3" + css-tree "^1.1.2" + csso "^4.2.0" + stable "^0.1.8" + +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.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^4.4.12: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +tar@^6.0.2, tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= + +temp-write@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== + dependencies: + graceful-fs "^4.1.15" + is-stream "^2.0.0" + make-dir "^3.0.0" + temp-dir "^1.0.0" + uuid "^3.3.2" + +temp@^0.8.1: + version "0.8.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= + dependencies: + execa "^0.7.0" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser-webpack-plugin@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser-webpack-plugin@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz#51d295eb7cc56785a67a372575fdc46e42d5c20c" + integrity sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q== + dependencies: + jest-worker "^26.6.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.7.0" + +terser-webpack-plugin@^5.1.3: + version "5.1.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz#c369cf8a47aa9922bd0d8a94fe3d3da11a7678a1" + integrity sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA== + dependencies: + jest-worker "^27.0.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + terser "^5.7.0" + +terser@^4.1.2: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +terser@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" + integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217" + integrity sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc= + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-hashes@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tiny-hashes/-/tiny-hashes-1.0.1.tgz#ddbe9060312ddb4efe0a174bb3a27e1331c425a1" + integrity sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g== + +title-case-minors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/title-case-minors/-/title-case-minors-1.0.0.tgz#51f17037c294747a1d1cda424b5004c86d8eb115" + integrity sha1-UfFwN8KUdHodHNpCS1AEyG2OsRU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-capital-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-capital-case/-/to-capital-case-1.0.0.tgz#a57c5014fd5a37217cf05099ff8a421bbf9c9b7f" + integrity sha1-pXxQFP1aNyF88FCZ/4pCG7+cm38= + dependencies: + to-space-case "^1.0.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-no-case@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" + integrity sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-sentence-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-sentence-case/-/to-sentence-case-1.0.0.tgz#c483bf3647737e5c738ef7006fe360d5f99c572e" + integrity sha1-xIO/NkdzflxzjvcAb+Ng1fmcVy4= + dependencies: + to-no-case "^1.0.0" + +to-space-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17" + integrity sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc= + dependencies: + to-no-case "^1.0.0" + +to-title-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-title-case/-/to-title-case-1.0.0.tgz#aca88f89d6064de50108a97cea0db44827e80061" + integrity sha1-rKiPidYGTeUBCKl86g20SCfoAGE= + dependencies: + escape-regexp-component "^1.0.2" + title-case-minors "^1.0.0" + to-capital-case "^1.0.0" + to-sentence-case "^1.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + +trim-off-newlines@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" + integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= + +ts-custom-error@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-3.2.0.tgz#ff8f80a3812bab9dc448536312da52dce1b720fb" + integrity sha512-cBvC2QjtvJ9JfWLvstVnI45Y46Y5dMxIaG1TDMGAD/R87hpvqFL+7LhvUDhnRCfOnx/xitollFWWvUKKKhbN0A== + +ts-jest@^26.5.5: + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + jest-util "^26.1.0" + json5 "2.x" + lodash "4.x" + make-error "1.x" + mkdirp "1.x" + semver "7.x" + yargs-parser "20.x" + +ts-loader@^8.0.17: + version "8.3.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33" + integrity sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^4.0.0" + loader-utils "^2.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +ts-node@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + dependencies: + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + +tsutils@^3.17.1, tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@^4.0.5, typescript@^4.1.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" + integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== + +uglify-js@^3.1.4: + version "3.13.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" + integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg== + +uid-number@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= + +umask@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +unfetch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-3.1.2.tgz#dc271ef77a2800768f7b459673c5604b5101ef77" + integrity sha512-L0qrK7ZeAudGiKYw6nzFjnJ2D5WHblUBwmHIqtPS6oKUd+Hcpk7/hKsSmcHsTlpd1TbTNsiRBUKRq3bHLNIqIw== + +unfetch@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + +universalify@^0.1.0, universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +unzip-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + integrity sha1-uYTwh3/AqJwsdzzB73tbIytbBv4= + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upath@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== + +update-check@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" + integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ== + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + +update-notifier@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.6.3.tgz#776dec8daa13e962a341e8a1d98354306b67ae08" + integrity sha1-d23sjaoT6WKjQeih2YNUMGtnrgg= + dependencies: + boxen "^0.3.1" + chalk "^1.0.0" + configstore "^2.0.0" + is-npm "^1.0.0" + latest-version "^2.0.0" + semver-diff "^2.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util-promisify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= + dependencies: + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utils-define-read-only-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-define-read-only-property/-/utils-define-read-only-property-1.0.0.tgz#2e5db55acb0a9f06f5c81bad9f426d0e24f8e16f" + integrity sha1-Ll21WssKnwb1yButn0JtDiT44W8= + +utils-is-little-endian@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-is-little-endian/-/utils-is-little-endian-1.0.0.tgz#a111d6e23593d3d228b0254664cd35c56dd5ad27" + integrity sha1-oRHW4jWT09IosCVGZM01xW3VrSc= + dependencies: + minimist "^1.2.0" + pkginfo "^0.3.1" + update-notifier "^0.6.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +v8-to-istanbul@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + +validate.io-infinite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/validate.io-infinite/-/validate.io-infinite-1.0.0.tgz#9076cf0b41f7ac201a42d58697c461142b4afcfb" + integrity sha1-kHbPC0H3rCAaQtWGl8RhFCtK/Ps= + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1, vm-browserify@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" + +watchpack@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" + integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wcwidth@^1.0.0, wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +webpack-bundle-analyzer@^4.4.1, webpack-bundle-analyzer@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.2.tgz#39898cf6200178240910d629705f0f3493f7d666" + integrity sha512-PIagMYhlEzFfhMYOzs5gFT55DkUdkyrJi/SxJp8EF3YMWhS+T9vvs2EoTetpk5qb6VsCq02eXTlRDOydRhDFAQ== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-cli@^4.4.0: + version "4.7.2" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.2.tgz#a718db600de6d3906a4357e059ae584a89f4c1a5" + integrity sha512-mEoLmnmOIZQNiRl0ebnjzQ74Hk0iKS5SiEEnpq3dRezoyR3yPaeQZCMCe+db4524pj1Pd5ghZXjT41KLzIhSLw== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.0.4" + "@webpack-cli/info" "^1.3.0" + "@webpack-cli/serve" "^1.5.1" + colorette "^1.2.1" + commander "^7.0.0" + execa "^5.0.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + v8-compile-cache "^2.2.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa" + integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@^4.44.1: + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.5.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + +webpack@^5.36.1: + version "5.41.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.41.1.tgz#23fa1d82c95c222d3fc3163806b9a833fe52b253" + integrity sha512-AJZIIsqJ/MVTmegEq9Tlw5mk5EHdGiJbDdz9qP15vmUH+oxI1FdWcL0E9EO8K/zKaRPWqEs7G/OPxq1P61u5Ug== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.48" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/wasm-edit" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + acorn "^8.2.1" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.8.0" + es-module-lexer "^0.6.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.2.0" + webpack-sources "^2.3.0" + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +widest-line@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" + integrity sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw= + dependencies: + string-width "^1.0.1" + +widest-line@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" + integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== + dependencies: + string-width "^2.1.1" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@>=0.0.2, wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +wrap-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" + integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== + dependencies: + ansi-styles "^3.2.0" + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^1.1.2: + version "1.3.4" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" + integrity sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8= + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + +write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write-json-file@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== + dependencies: + detect-indent "^5.0.0" + graceful-fs "^4.1.15" + make-dir "^2.1.0" + pify "^4.0.1" + sort-keys "^2.0.0" + write-file-atomic "^2.4.2" + +write-json-file@^4.1.1, write-json-file@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" + integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== + dependencies: + detect-indent "^6.0.0" + graceful-fs "^4.1.15" + is-plain-obj "^2.0.0" + make-dir "^3.0.0" + sort-keys "^4.0.0" + write-file-atomic "^3.0.0" + +write-pkg@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" + integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== + dependencies: + sort-keys "^2.0.0" + type-fest "^0.4.1" + write-json-file "^3.2.0" + +ws@^7.3.1, ws@^7.4.5: + version "7.5.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66" + integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow== + +xdg-basedir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" + integrity sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I= + dependencies: + os-homedir "^1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +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== + +xregexp@^4.2.4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.4.1.tgz#c84a88fa79e9ab18ca543959712094492185fe65" + integrity sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag== + dependencies: + "@babel/runtime-corejs3" "^7.12.1" + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==