diff --git a/.gitignore b/.gitignore index 33beccb3..d113ba0f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ coverage dist build_extension .note +.DS_Store .idea \ No newline at end of file diff --git a/client/.babelrc b/client/.babelrc index e65d0b85..eb143d8d 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -28,6 +28,7 @@ "@cd/selectors": "./src/selectors", "@cd/services": "./src/services", "@cd/shared": "./src/shared", + "@cd/libs": "./src/libs", "@cd/store": "./src/store", "@cd/config": "./src/config", "@cd/constants": "./src/constants", diff --git a/client/.env b/client/.env index 43f03c80..b00fb659 100644 --- a/client/.env +++ b/client/.env @@ -1,2 +1,3 @@ SKIP_PREFLIGHT_CHECK=true NODE_ENV=development +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/client/.env.development.local b/client/.env.development.local index 41c045b0..4e607fc3 100644 --- a/client/.env.development.local +++ b/client/.env.development.local @@ -1,4 +1,5 @@ REACT_APP_API_ROOT=http://localhost:3001 REACT_APP_AUCTION_HASH=93d923e336b20a4c4ca14d592b60e5bd3fe330775618290104f9beb326db7ae2 REACT_APP_DEBUG_ENV=true -AUTO_LOCK_TIMEOUT_ALARM=10 \ No newline at end of file +AUTO_LOCK_TIMEOUT_ALARM=10 +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/client/.env.production.local b/client/.env.production.local index 2abc7bf5..a40c2e00 100644 --- a/client/.env.production.local +++ b/client/.env.production.local @@ -4,3 +4,4 @@ REACT_APP_AUCTION_HASH=ccb576d6ce6dec84a551e48f0d0b7af89ddba44c7390b690036257a04 REACT_APP_AVAILABLE_FEATURES=["home","dashboard","history","nfts","tokens","staking"] APP_ENVIRONMENT=prod REACT_APP_VERSION=1.6.2 +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/client/jest.config.js b/client/jest.config.js index ddba9d1a..9ea5c7b6 100644 --- a/client/jest.config.js +++ b/client/jest.config.js @@ -22,6 +22,7 @@ const config = { "^@cd/actions(.*)$": "/src/actions$1", "^@cd/helpers(.*)$": "/src/helpers$1", "^@cd/selectors(.*)$": "/src/selectors$1", + "^@cd/libs(.*)$": "/src/libs$1", "^@cd/services(.*)$": "/src/services$1", "^@cd/shared(.*)$": "/src/shared$1", "^@cd/store(.*)$": "/src/store$1", diff --git a/client/package.json b/client/package.json index b08c6d6a..eeafd452 100644 --- a/client/package.json +++ b/client/package.json @@ -13,6 +13,8 @@ "@popperjs/core": "2.11.6", "@redux-requests/axios": "^1.1.1", "@redux-requests/core": "^1.6.2", + "@sentry/browser": "^7.59.3", + "@sentry/integrations": "^7.60.0", "@tanstack/react-query": "^4.29.5", "@zondax/ledger-casper": "^0.0.1", "apexcharts": "^3.30.0", diff --git a/client/src/app/web-extension/App.js b/client/src/app/web-extension/App.js index 4e3d3f46..907b9bbc 100644 --- a/client/src/app/web-extension/App.js +++ b/client/src/app/web-extension/App.js @@ -15,6 +15,8 @@ import 'react-toastify/dist/ReactToastify.css'; import '../../../node_modules/bootstrap/dist/css/bootstrap.min.css'; import './App.scss'; +import '@cd/libs/installSentry'; + const getRoutes = (routes) => { return ( routes && diff --git a/client/src/components/web-extension/Common/CreatePasswordPage/useCreateUser.js b/client/src/components/web-extension/Common/CreatePasswordPage/useCreateUser.js index 68fb9ee4..25c47994 100644 --- a/client/src/components/web-extension/Common/CreatePasswordPage/useCreateUser.js +++ b/client/src/components/web-extension/Common/CreatePasswordPage/useCreateUser.js @@ -9,6 +9,7 @@ import { selectCreateWalletDerivationPath, } from '@cd/selectors/createWallet'; import { createUserServiceSW } from '@cd/components/hooks/useServiceWorker'; +import { ERRORS } from '@cd/constants/errors'; const useCreateUser = () => { const navigate = useNavigate(); @@ -34,7 +35,7 @@ const useCreateUser = () => { async (password) => { try { if (!keyPhrase) { - throw new Error('Missing keyphrase'); + throw new Error(ERRORS.MISSING_KEYPHRASE); } const result = await createUserServiceSW(password, keyPhrase, encryptionType, derivationPath); diff --git a/client/src/config/index.js b/client/src/config/index.js index 1046f8d3..a0e81eae 100644 --- a/client/src/config/index.js +++ b/client/src/config/index.js @@ -27,6 +27,10 @@ const CONFIG_OPTIONS = { env: 'REACT_APP_EXPLORER_ROOT_LINK', default: 'https://testnet.cspr.live', }, + SENTRY_DSN: { + env: 'REACT_APP_SENTRY_DSN', + default: '', + } }; const getConfigOption = (option) => { diff --git a/client/src/constants/errors.js b/client/src/constants/errors.js new file mode 100644 index 00000000..2907c52f --- /dev/null +++ b/client/src/constants/errors.js @@ -0,0 +1,11 @@ +export const ERRORS = { + MISSING_KEYPHRASE: 'Missing keyphrase', + YOUR_ACCOUNT_HAS_NOT_BEEN_CREATED: 'Your account has not been created', + PROVIDED_TARGET_PUBLIC_KEY_DOESNT_MATCH: 'Provided target public key doesn\'t match the one in deploy', + PROVIDED_TARGET_PUBLIC_KEY_DOESNT_MATCH_DEPLOY: 'The provided target public key does not match the one specified in the deploy', + TARGET_SPECIFIED_IN_DEPLOY_NOT_CORRECT_FORMAT: 'The target specified in the deploy is not in the correct format, it must be either an AccountHash or a PublicKey', + FAILED_TO_PARSE_KEY_ARGUMENT: 'Failed to parse key argument', + MISSING_USER_SERVICE_INSTANCE: 'Missing UserService instance', + THIS_SITE_IS_NOT_CONNECTED: 'This site is not connected', + CAN_NOT_SIGN_MESSAGE: 'Can not sign message', +} \ No newline at end of file diff --git a/client/src/helpers/extension/signing.js b/client/src/helpers/extension/signing.js index 8093fa75..e5f524f8 100644 --- a/client/src/helpers/extension/signing.js +++ b/client/src/helpers/extension/signing.js @@ -6,6 +6,7 @@ import { CLPublicKey } from 'casper-js-sdk'; import browser from 'webextension-polyfill'; +import { ERRORS } from '@cd/constants/errors'; export async function updateStatusEvent(tabId, msg, { isUnlocked, @@ -46,7 +47,7 @@ export function verifyTargetAccountMatch( if (providedTargetKeyHash !== targetAccountHash) { throw new Error( - "Provided target public key doesn't match the one in deploy" + ERRORS.PROVIDED_TARGET_PUBLIC_KEY_DOESNT_MATCH ); } } @@ -120,12 +121,12 @@ export const parseTransferData = (transferDeploy, providedTarget) => { case CLTypeTag.PublicKey: targetFromDeployHex = targetFromDeploy.toHex(); if (providedTarget && targetFromDeployHex.toLowerCase() !== providedTarget.toLowerCase()) { - throw new Error("The provided target public key does not match the one specified in the deploy."); + throw new Error(ERRORS.PROVIDED_TARGET_PUBLIC_KEY_DOESNT_MATCH_DEPLOY); } transferArgs['Recipient (Key)'] = targetFromDeployHex; break; default: - throw new Error('The target specified in the deploy is not in the correct format, it must be either an AccountHash or a PublicKey.'); + throw new Error(ERRORS.TARGET_SPECIFIED_IN_DEPLOY_NOT_CORRECT_FORMAT); } transferArgs['Amount'] = transferDeploy.getArgByName('amount').value().toString(); @@ -160,7 +161,7 @@ export function parseDeployArg(arg) { if (key.isAccount() || key.isURef() || key.isHash()) { return parseDeployArg(value); } - throw new Error('Failed to parse key argument'); + throw new Error(ERRORS.FAILED_TO_PARSE_KEY_ARGUMENT); } case CLTypeTag.URef: diff --git a/client/src/libs/installSentry.js b/client/src/libs/installSentry.js new file mode 100644 index 00000000..e0b8b134 --- /dev/null +++ b/client/src/libs/installSentry.js @@ -0,0 +1,3 @@ +import { setupSentry } from './setupSentry'; + +setupSentry(); \ No newline at end of file diff --git a/client/src/libs/setupSentry.js b/client/src/libs/setupSentry.js new file mode 100644 index 00000000..a1430dfa --- /dev/null +++ b/client/src/libs/setupSentry.js @@ -0,0 +1,33 @@ +import config from '@cd/config'; +import { getVersion } from '@cd/helpers/key'; +import * as Sentry from '@sentry/browser'; +import { Dedupe as DedupeIntegration } from "@sentry/integrations"; +import { ERRORS } from '@cd/constants/errors'; + +const IGNORE_ERRORS = [ + ...Object.values(ERRORS), +]; + +export const setupSentry = () => { + Sentry.init({ + dsn: config.SENTRY_DSN, + environment: config.APP_ENVIRONMENT, + release: 'casperdash-extension@' + getVersion(), + beforeSend(event, hint) { + const error = hint.originalException; + if ( + error && + error.message && + error.message.match(new RegExp(IGNORE_ERRORS.join('|'), 'i')) + ) { + + return null; + } + + return event; + }, + integrations: [ + new DedupeIntegration(), + ], + }); +}; \ No newline at end of file diff --git a/client/src/services/ServiceWorker/Controllers/AccountController.js b/client/src/services/ServiceWorker/Controllers/AccountController.js index b05b3b67..521f7ed5 100644 --- a/client/src/services/ServiceWorker/Controllers/AccountController.js +++ b/client/src/services/ServiceWorker/Controllers/AccountController.js @@ -3,6 +3,7 @@ import { DeployUtil, signFormattedMessage } from 'casper-js-sdk'; import _get from 'lodash-es/get'; import UserService from '@cd/services/ServiceWorker/UserService'; import { getConnectedAccountChromeLocalStorage, cacheLoginInfoToLocalStorage } from '@cd/actions/userActions.utils'; +import { ERRORS } from '@cd/constants/errors'; class AccountController { /** * Only available after creating new User or successfully @@ -81,7 +82,7 @@ class AccountController { getPublicKey = async () => { if (!this.userService) { - throw new Error('Missing UserService instance'); + throw new Error(ERRORS.MISSING_USER_SERVICE_INSTANCE); } return this.userService.getPublicKey(); diff --git a/client/src/services/ServiceWorker/Controllers/PopupController.js b/client/src/services/ServiceWorker/Controllers/PopupController.js index e0583ac4..c1570017 100644 --- a/client/src/services/ServiceWorker/Controllers/PopupController.js +++ b/client/src/services/ServiceWorker/Controllers/PopupController.js @@ -5,6 +5,7 @@ import _isEmpty from 'lodash-es/isEmpty'; import _get from 'lodash-es/get'; import { getLastError, updateStatusEvent } from '@cd/helpers/extension/signing'; import { getConnectedAccountChromeLocalStorage } from '@cd/actions/userActions.utils'; +import { ERRORS } from '@cd/constants/errors'; const CONNECTED_SITES = 'connectedSites'; const BLACKLIST_PROTOCOLS = ['chrome-extension:', 'chrome:']; @@ -92,7 +93,7 @@ class PopupController { const account = await getConnectedAccountChromeLocalStorage(); const loginOptions = _get(account, 'loginOptions', null); if (!loginOptions) { - throw new Error('Your account has not been created.'); + throw new Error(ERRORS.YOUR_ACCOUNT_HAS_NOT_BEEN_CREATED); } const isConnected = await this.isConnected({ origin }); diff --git a/client/src/services/ServiceWorker/Controllers/SigningController.js b/client/src/services/ServiceWorker/Controllers/SigningController.js index 121105b6..6adbf594 100644 --- a/client/src/services/ServiceWorker/Controllers/SigningController.js +++ b/client/src/services/ServiceWorker/Controllers/SigningController.js @@ -2,6 +2,7 @@ import EventEmitter from 'events'; import { DeployUtil, encodeBase16, formatMessageWithHeaders } from 'casper-js-sdk'; import _pick from 'lodash-es/pick'; import { getDeployArgs, getDeployPayment, getDeployType } from '@cd/helpers/extension/signing'; +import { ERRORS } from '@cd/constants/errors'; const SIGNING_STATUS = { unsigned: 'Unsigned', @@ -24,7 +25,7 @@ class SigningController extends EventEmitter { getActivePublicKey = async ({ origin }) => { const isConnected = await this.popupController.isConnected({ origin }); if (!isConnected) { - throw new Error('This site is not connected'); + throw new Error(ERRORS.THIS_SITE_IS_NOT_CONNECTED); } return this.accountController.getCurrentPublicKey(); @@ -33,7 +34,7 @@ class SigningController extends EventEmitter { signDeploy = async ({ deploy, signingPublicKeyHex, targetPublicKeyHex, origin }) => { const isConnected = await this.popupController.isConnected({ origin }); if (!isConnected) { - throw new Error('This site is not connected'); + throw new Error(ERRORS.THIS_SITE_IS_NOT_CONNECTED); } let innerDeploy = DeployUtil.deployFromJson(deploy); @@ -128,7 +129,7 @@ class SigningController extends EventEmitter { signMessage = async ({ message, signingPublicKey, origin }) => { const isConnected = await this.popupController.isConnected({ origin }); if (!isConnected) { - throw new Error('This site is not connected'); + throw new Error(ERRORS.THIS_SITE_IS_NOT_CONNECTED); } let messageBytes; @@ -163,7 +164,7 @@ class SigningController extends EventEmitter { return reject(new Error(message)); } default: - reject(new Error('Can not sign message')); + reject(new Error(ERRORS.CAN_NOT_SIGN_MESSAGE)); } }); }); diff --git a/client/src/services/stakeServices.js b/client/src/services/stakeServices.js index 78d08b9b..12728448 100644 --- a/client/src/services/stakeServices.js +++ b/client/src/services/stakeServices.js @@ -63,7 +63,6 @@ export const getStakeDeploy = ({ network, ); } catch (error) { - console.error(error); throw new Error(`Failed to get stake deploy.`); } }; diff --git a/client/src/store/index.js b/client/src/store/index.js index 7a230028..07a0cfb2 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -1,6 +1,7 @@ import axios from 'axios'; import { handleRequests } from '@redux-requests/core'; import { createDriver } from '@redux-requests/axios'; +import * as Sentry from '@sentry/browser'; import { combineReducers, createStore, applyMiddleware, compose } from 'redux'; import { persistStore, persistReducer } from 'redux-persist'; import webLocalStorage from 'redux-persist/lib/storage'; @@ -94,6 +95,12 @@ const { requestsReducer, requestsMiddleware } = handleRequests({ }, onError: (error, action, store) => { store.dispatch(removeLoadingStatus(action.type)); + + Sentry.withScope(function (scope) { + scope.setTag('api', 'true'); + Sentry.captureException(error); + }); + throw error; }, onAbort: (action, store) => { diff --git a/client/template/extension/manifest.json b/client/template/extension/manifest.json index bfcd99d2..c971021f 100644 --- a/client/template/extension/manifest.json +++ b/client/template/extension/manifest.json @@ -12,7 +12,7 @@ "default_popup": "popup.html" }, "content_security_policy": { - "extension_pages": "default-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; script-src 'self'; img-src http: https: data:; connect-src https://testnet-api.casperdash.io https://api.casperdash.io https://cdn.jsdelivr.net https://api.cspr.live; style-src 'unsafe-inline'" + "extension_pages": "default-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; script-src 'self'; img-src http: https: data:; connect-src https://testnet-api.casperdash.io https://api.casperdash.io https://cdn.jsdelivr.net https://api.cspr.live https://o4505474114715648.ingest.sentry.io; style-src 'unsafe-inline'" }, "content_scripts": [ { diff --git a/client/yarn.lock b/client/yarn.lock index 2149fbe0..b43c2cd9 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1977,6 +1977,70 @@ "@noble/hashes" "~1.3.0" "@scure/base" "~1.1.0" +"@sentry-internal/tracing@7.73.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.73.0.tgz#4838f31e41d23a6041ef4520519b80f788bf1cac" + integrity sha512-ig3WL/Nqp8nRQ52P205NaypGKNfIl/G+cIqge9xPW6zfRb5kJdM1YParw9GSJ1SPjEZBkBORGAML0on5H2FILw== + dependencies: + "@sentry/core" "7.73.0" + "@sentry/types" "7.73.0" + "@sentry/utils" "7.73.0" + tslib "^2.4.1 || ^1.9.3" + +"@sentry/browser@^7.59.3": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.73.0.tgz#a8eaeb50cf16ca32f0039a81719c503d7045495f" + integrity sha512-e301hUixcJ5+HNKCJwajFF5smF4opXEFSclyWsJuFNufv5J/1C1SDhbwG2JjBt5zzdSoKWJKT1ewR6vpICyoDw== + dependencies: + "@sentry-internal/tracing" "7.73.0" + "@sentry/core" "7.73.0" + "@sentry/replay" "7.73.0" + "@sentry/types" "7.73.0" + "@sentry/utils" "7.73.0" + tslib "^2.4.1 || ^1.9.3" + +"@sentry/core@7.73.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.73.0.tgz#1caeeec44f42c4d58c06cc05dec39e5497b65aa3" + integrity sha512-9FEz4Gq848LOgVN2OxJGYuQqxv7cIVw69VlAzWHEm3njt8mjvlTq+7UiFsGRo84+59V2FQuHxzA7vVjl90WfSg== + dependencies: + "@sentry/types" "7.73.0" + "@sentry/utils" "7.73.0" + tslib "^2.4.1 || ^1.9.3" + +"@sentry/integrations@^7.60.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.73.0.tgz#f12d4a6422787cc6d50471bbdec52ed32611a444" + integrity sha512-IjVpn4d+aSL9L1Ntu/oAdRwujz4BzzavDsZf96Xgc/AjBnjAEUT+wT1dAwluThfuKDXmWOJHhZ2cHHMfqI+7vw== + dependencies: + "@sentry/core" "7.73.0" + "@sentry/types" "7.73.0" + "@sentry/utils" "7.73.0" + localforage "^1.8.1" + tslib "^2.4.1 || ^1.9.3" + +"@sentry/replay@7.73.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.73.0.tgz#4e6c522bac5c12f596ef76afe15ecb3807407669" + integrity sha512-a8IC9SowBisLYD2IdLkXzx7gN4iVwHDJhQvLp2B8ARs1PyPjJ7gCxSMHeGrYp94V0gOXtorNYkrxvuX8ayPROA== + dependencies: + "@sentry/core" "7.73.0" + "@sentry/types" "7.73.0" + "@sentry/utils" "7.73.0" + +"@sentry/types@7.73.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.73.0.tgz#6d811bbe413d319df0a592a672d6d72a94a8e716" + integrity sha512-/v8++bly8jW7r4cP2wswYiiVpn7eLLcqwnfPUMeCQze4zj3F3nTRIKc9BGHzU0V+fhHa3RwRC2ksqTGq1oJMDg== + +"@sentry/utils@7.73.0": + version "7.73.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.73.0.tgz#530cf023f7c395aa7708cd3824e5a45948449c10" + integrity sha512-h3ZK/qpf4k76FhJV9uiSbvMz3V/0Ovy94C+5/9UgPMVCJXFmVsdw8n/dwANJ7LupVPfYP23xFGgebDMFlK1/2w== + dependencies: + "@sentry/types" "7.73.0" + tslib "^2.4.1 || ^1.9.3" + "@sheerun/mutationobserver-shim@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25" @@ -6037,6 +6101,11 @@ ignore@^5.1.9, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + immutable@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" @@ -7108,6 +7177,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + lilconfig@2.1.0, lilconfig@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" @@ -7160,6 +7236,13 @@ loader-utils@^2.0.0, loader-utils@^2.0.4: emojis-list "^3.0.0" json5 "^2.1.2" +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -9636,6 +9719,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.4.0, tslib@^2.4.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== +"tslib@^2.4.1 || ^1.9.3": + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tty-browserify@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" diff --git a/jsconfig.json b/jsconfig.json index 6a011177..1fb85b8a 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -8,6 +8,7 @@ "@cd/actions/*": ["client/src/actions/*"], "@cd/helpers/*": ["client/src/helpers/*"], "@cd/selectors/*": ["client/src/selectors/*"], + "@cd/libs/*": ["client/src/libs/*"], "@cd/services/*": ["client/src/services/*"], "@cd/shared/*": ["client/src/shared/*"], "@cd/store/*": ["client/src/store/*"],