@imtbl/sdk 0.37.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ class ImmutableConfiguration {
39
39
  }
40
40
  }
41
41
  const API_KEY_PREFIX = 'sk_imapik-';
42
- const PUBLISHABLE_KEY_PREFIX = 'pk_imapik-';
42
+ const PUBLISHABLE_KEY_PREFIX$1 = 'pk_imapik-';
43
43
  const addApiKeyToAxiosHeader = (apiKey) => {
44
44
  if (!apiKey.startsWith(API_KEY_PREFIX)) {
45
45
  throw new Error('Invalid API key. Create your api key in Immutable developer hub. https://hub.immutable.com');
@@ -47,7 +47,7 @@ const addApiKeyToAxiosHeader = (apiKey) => {
47
47
  globalAxios$1.defaults.headers.common['x-immutable-api-key'] = apiKey;
48
48
  };
49
49
  const addPublishableKeyToAxiosHeader = (publishableKey) => {
50
- if (!publishableKey.startsWith(PUBLISHABLE_KEY_PREFIX)) {
50
+ if (!publishableKey.startsWith(PUBLISHABLE_KEY_PREFIX$1)) {
51
51
  throw new Error('Invalid Publishable key. Create your Publishable key in Immutable developer hub.'
52
52
  + ' https://hub.immutable.com');
53
53
  }
@@ -4234,7 +4234,7 @@ class MultiRollupApiClients {
4234
4234
  }
4235
4235
 
4236
4236
  // eslint-disable-next-line @typescript-eslint/naming-convention
4237
- const defaultHeaders$1 = { 'x-sdk-version': 'ts-immutable-sdk-0.37.0' };
4237
+ const defaultHeaders$1 = { 'x-sdk-version': 'ts-immutable-sdk-0.39.0' };
4238
4238
  const createConfig = ({ basePath, headers, }) => {
4239
4239
  if (!basePath.trim()) {
4240
4240
  throw Error('basePath can not be empty');
@@ -4294,7 +4294,7 @@ class APIError extends Error {
4294
4294
 
4295
4295
  /* eslint-disable implicit-arrow-linebreak */
4296
4296
  const defaultHeaders = {
4297
- sdkVersion: 'ts-immutable-sdk-multi-rollup-api-client-0.37.0',
4297
+ sdkVersion: 'ts-immutable-sdk-multi-rollup-api-client-0.39.0',
4298
4298
  };
4299
4299
  /**
4300
4300
  * createAPIConfiguration to create a custom Configuration
@@ -4733,7 +4733,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
4733
4733
  chainID,
4734
4734
  coreContractAddress,
4735
4735
  registrationContractAddress,
4736
- sdkVersion: 'ts-immutable-sdk-0.37.0',
4736
+ sdkVersion: 'ts-immutable-sdk-0.39.0',
4737
4737
  });
4738
4738
  /**
4739
4739
  * Sets `sdkVersion` at the time of build
@@ -34780,6 +34780,7 @@ var ConnectEventType;
34780
34780
  ConnectEventType["CLOSE_WIDGET"] = "close-widget";
34781
34781
  ConnectEventType["SUCCESS"] = "success";
34782
34782
  ConnectEventType["FAILURE"] = "failure";
34783
+ ConnectEventType["LANGUAGE_CHANGED"] = "language-changed";
34783
34784
  })(ConnectEventType || (ConnectEventType = {}));
34784
34785
 
34785
34786
  /**
@@ -34790,6 +34791,7 @@ var WalletEventType;
34790
34791
  WalletEventType["CLOSE_WIDGET"] = "close-widget";
34791
34792
  WalletEventType["NETWORK_SWITCH"] = "network-switch";
34792
34793
  WalletEventType["DISCONNECT_WALLET"] = "disconnect-wallet";
34794
+ WalletEventType["LANGUAGE_CHANGED"] = "language-changed";
34793
34795
  })(WalletEventType || (WalletEventType = {}));
34794
34796
 
34795
34797
  /**
@@ -34801,6 +34803,7 @@ var SwapEventType;
34801
34803
  SwapEventType["SUCCESS"] = "success";
34802
34804
  SwapEventType["FAILURE"] = "failure";
34803
34805
  SwapEventType["REJECTED"] = "rejected";
34806
+ SwapEventType["LANGUAGE_CHANGED"] = "language-changed";
34804
34807
  })(SwapEventType || (SwapEventType = {}));
34805
34808
 
34806
34809
  /**
@@ -34813,6 +34816,7 @@ var SaleEventType;
34813
34816
  SaleEventType["FAILURE"] = "failure";
34814
34817
  SaleEventType["REJECTED"] = "rejected";
34815
34818
  SaleEventType["TRANSACTION_SUCCESS"] = "transaction-success";
34819
+ SaleEventType["LANGUAGE_CHANGED"] = "language-changed";
34816
34820
  })(SaleEventType || (SaleEventType = {}));
34817
34821
 
34818
34822
  /**
@@ -34823,6 +34827,7 @@ var BridgeEventType;
34823
34827
  BridgeEventType["CLOSE_WIDGET"] = "close-widget";
34824
34828
  BridgeEventType["FAILURE"] = "failure";
34825
34829
  BridgeEventType["TRANSACTION_SENT"] = "transaction-sent";
34830
+ BridgeEventType["LANGUAGE_CHANGED"] = "language-changed";
34826
34831
  })(BridgeEventType || (BridgeEventType = {}));
34827
34832
 
34828
34833
  /**
@@ -34845,6 +34850,7 @@ var OnRampEventType;
34845
34850
  OnRampEventType["CLOSE_WIDGET"] = "close-widget";
34846
34851
  OnRampEventType["SUCCESS"] = "success";
34847
34852
  OnRampEventType["FAILURE"] = "failure";
34853
+ OnRampEventType["LANGUAGE_CHANGED"] = "language-changed";
34848
34854
  })(OnRampEventType || (OnRampEventType = {}));
34849
34855
 
34850
34856
  /**
@@ -35233,7 +35239,11 @@ async function addNetworkToWallet(networkMap, web3Provider, chainId) {
35233
35239
  async function getNetworkAllowList(config, { type = NetworkFilterTypes.ALL, exclude }) {
35234
35240
  const { networkMap } = config;
35235
35241
  const allowedNetworkConfig = (await config.remote.getConfig('allowedNetworks'));
35236
- const list = allowedNetworkConfig.filter((network) => {
35242
+ if (!allowedNetworkConfig) {
35243
+ // eslint-disable-next-line no-console
35244
+ console.warn('No allowed networks configured');
35245
+ }
35246
+ const list = (allowedNetworkConfig || []).filter((network) => {
35237
35247
  const allowAllTokens = type === NetworkFilterTypes.ALL;
35238
35248
  const networkNotExcluded = !(exclude || [])
35239
35249
  .map((exc) => exc.chainId)
@@ -35673,32 +35683,21 @@ const GAS_OVERRIDES = {
35673
35683
  maxPriorityFeePerGas: BigNumber$1.from(101e9),
35674
35684
  };
35675
35685
 
35676
- const SDK_VERSION_MARKER = '0.37.0';
35686
+ const SDK_VERSION_MARKER = '0.39.0';
35677
35687
  // This SDK version is replaced by the `yarn build` command ran on the root level
35678
35688
  const globalPackageVersion = () => SDK_VERSION_MARKER;
35679
35689
 
35680
35690
  class RemoteConfigFetcher {
35691
+ httpClient;
35681
35692
  isDevelopment;
35682
35693
  isProduction;
35683
35694
  configCache;
35684
35695
  tokensCache;
35685
35696
  version = 'v1';
35686
- constructor(params) {
35697
+ constructor(httpClient, params) {
35687
35698
  this.isDevelopment = params.isDevelopment;
35688
35699
  this.isProduction = params.isProduction;
35689
- }
35690
- static async makeHttpRequest(url) {
35691
- let response;
35692
- try {
35693
- response = await globalAxios$1.get(url);
35694
- }
35695
- catch (error) {
35696
- throw new CheckoutError(`Error fetching from api: ${error.message}`, CheckoutErrorType.API_ERROR);
35697
- }
35698
- if (response.status !== 200) {
35699
- throw new CheckoutError(`Error fetching from api: ${response.status} ${response.statusText}`, CheckoutErrorType.API_ERROR);
35700
- }
35701
- return response;
35700
+ this.httpClient = httpClient;
35702
35701
  }
35703
35702
  getEndpoint = () => {
35704
35703
  if (this.isDevelopment)
@@ -35707,18 +35706,33 @@ class RemoteConfigFetcher {
35707
35706
  return CHECKOUT_CDN_BASE_URL[Environment$1.PRODUCTION];
35708
35707
  return CHECKOUT_CDN_BASE_URL[Environment$1.SANDBOX];
35709
35708
  };
35709
+ // eslint-disable-next-line class-methods-use-this
35710
+ parseResponse(response) {
35711
+ let responseData = response.data;
35712
+ if (response.data && typeof response.data !== 'object') {
35713
+ try {
35714
+ responseData = JSON.parse(response.data);
35715
+ }
35716
+ catch (jsonError) {
35717
+ throw new CheckoutError('Invalid configuration', CheckoutErrorType.API_ERROR);
35718
+ }
35719
+ }
35720
+ return responseData;
35721
+ }
35710
35722
  async loadConfig() {
35711
35723
  if (this.configCache)
35712
35724
  return this.configCache;
35713
- const response = await RemoteConfigFetcher.makeHttpRequest(`${this.getEndpoint()}/${this.version}/config`);
35714
- this.configCache = response.data;
35725
+ const response = await this.httpClient.get(`${this.getEndpoint()}/${this.version}/config`);
35726
+ // Ensure that the configuration is valid
35727
+ this.configCache = this.parseResponse(response);
35715
35728
  return this.configCache;
35716
35729
  }
35717
35730
  async loadConfigTokens() {
35718
35731
  if (this.tokensCache)
35719
35732
  return this.tokensCache;
35720
- const response = await RemoteConfigFetcher.makeHttpRequest(`${this.getEndpoint()}/${this.version}/config/tokens`);
35721
- this.tokensCache = response.data;
35733
+ const response = await this.httpClient.get(`${this.getEndpoint()}/${this.version}/config/tokens`);
35734
+ // Ensure that the configuration is valid
35735
+ this.tokensCache = this.parseResponse(response);
35722
35736
  return this.tokensCache;
35723
35737
  }
35724
35738
  async getConfig(key) {
@@ -35735,6 +35749,7 @@ class RemoteConfigFetcher {
35735
35749
  return {};
35736
35750
  return config[chainId] ?? [];
35737
35751
  }
35752
+ getHttpClient = () => this.httpClient;
35738
35753
  }
35739
35754
 
35740
35755
  class CheckoutConfigurationError extends Error {
@@ -35785,7 +35800,7 @@ class CheckoutConfiguration {
35785
35800
  remote;
35786
35801
  environment;
35787
35802
  networkMap;
35788
- constructor(config) {
35803
+ constructor(config, httpClient) {
35789
35804
  if (!Object.values(Environment$1).includes(config.baseConfig.environment)) {
35790
35805
  throw new CheckoutConfigurationError('Invalid checkout configuration of environment');
35791
35806
  }
@@ -35796,7 +35811,7 @@ class CheckoutConfiguration {
35796
35811
  this.isSwapEnabled = config.swap?.enable ?? DEFAULT_SWAP_ENABLED;
35797
35812
  this.isBridgeEnabled = config.bridge?.enable ?? DEFAULT_BRIDGE_ENABLED;
35798
35813
  this.networkMap = networkMap(this.isProduction, this.isDevelopment);
35799
- this.remote = new RemoteConfigFetcher({
35814
+ this.remote = new RemoteConfigFetcher(httpClient, {
35800
35815
  isDevelopment: this.isDevelopment,
35801
35816
  isProduction: this.isProduction,
35802
35817
  });
@@ -35861,9 +35876,7 @@ class Blockscout {
35861
35876
  ttl;
35862
35877
  chainId;
35863
35878
  cacheMap;
35864
- static async makeHttpRequest(url) {
35865
- return globalAxios$1.get(url);
35866
- }
35879
+ httpClient;
35867
35880
  setCache(key, data) {
35868
35881
  this.cacheMap[key] = { data, ttl: new Date().getTime() + this.ttl * 1000 };
35869
35882
  }
@@ -35875,11 +35888,13 @@ class Blockscout {
35875
35888
  }
35876
35889
  /**
35877
35890
  * Blockscout constructor
35891
+ * @param httpClient Client used for http requests
35878
35892
  * @param chainId target chain
35879
35893
  * @param ttl cache TTL
35880
35894
  */
35881
- constructor(params) {
35882
- this.chainId = params.chainId;
35895
+ constructor(httpClient, chainId, ttl) {
35896
+ this.httpClient = httpClient;
35897
+ this.chainId = chainId;
35883
35898
  this.url = BLOCKSCOUT_CHAIN_URL_MAP[this.chainId].url;
35884
35899
  const native = BLOCKSCOUT_CHAIN_URL_MAP[this.chainId].nativeToken;
35885
35900
  this.nativeToken = {
@@ -35889,7 +35904,7 @@ class Blockscout {
35889
35904
  symbol: native.symbol,
35890
35905
  };
35891
35906
  this.cacheMap = {};
35892
- this.ttl = params.ttl !== undefined ? params.ttl : CACHE_DATA_TTL;
35907
+ this.ttl = ttl !== undefined ? ttl : CACHE_DATA_TTL;
35893
35908
  }
35894
35909
  /**
35895
35910
  * isChainSupported verifies if the chain is supported by Blockscout
@@ -35917,7 +35932,7 @@ class Blockscout {
35917
35932
  const cached = this.getCache(url);
35918
35933
  if (cached)
35919
35934
  return Promise.resolve(cached);
35920
- const response = await Blockscout.makeHttpRequest(url);
35935
+ const response = await this.httpClient.get(url);
35921
35936
  if (response.status >= 400) {
35922
35937
  return Promise.reject({
35923
35938
  code: response.status,
@@ -35958,7 +35973,7 @@ class Blockscout {
35958
35973
  const cached = this.getCache(url);
35959
35974
  if (cached)
35960
35975
  return Promise.resolve(cached);
35961
- const response = await Blockscout.makeHttpRequest(url);
35976
+ const response = await this.httpClient.get(url);
35962
35977
  if (response.status >= 400) {
35963
35978
  return Promise.reject({
35964
35979
  code: response.status,
@@ -36043,7 +36058,7 @@ const blockscoutClientMap = new Map();
36043
36058
  // This function is a utility function that can be used to reset the
36044
36059
  // blockscout map and therefore clear all the cache.
36045
36060
  const resetBlockscoutClientMap = () => blockscoutClientMap.clear();
36046
- const getIndexerBalance = async (walletAddress, chainId, filterTokens) => {
36061
+ const getIndexerBalance = async (config, walletAddress, chainId, filterTokens) => {
36047
36062
  // Shuffle the mapping of the tokens configuration so it is a hashmap
36048
36063
  // for faster access to tokens config objects.
36049
36064
  const shouldFilter = filterTokens !== undefined;
@@ -36051,7 +36066,8 @@ const getIndexerBalance = async (walletAddress, chainId, filterTokens) => {
36051
36066
  // Get blockscout client for the given chain
36052
36067
  let blockscoutClient = blockscoutClientMap.get(chainId);
36053
36068
  if (!blockscoutClient) {
36054
- blockscoutClient = new Blockscout({ chainId });
36069
+ const httpClient = config.remote.getHttpClient();
36070
+ blockscoutClient = new Blockscout(httpClient, chainId);
36055
36071
  blockscoutClientMap.set(chainId, blockscoutClient);
36056
36072
  }
36057
36073
  // Hold the items in an array for post-fetching processing
@@ -36186,7 +36202,7 @@ const getAllBalances = async (config, web3Provider, walletAddress, chainId) => {
36186
36202
  const isL1Chain = getL1ChainId(config) === chainId;
36187
36203
  if (!address)
36188
36204
  address = await web3Provider?.getSigner().getAddress();
36189
- return await measureAsyncExecution(config, `Time to fetch balances using blockscout for ${chainId}`, getIndexerBalance(address, chainId, isL1Chain ? tokens : undefined));
36205
+ return await measureAsyncExecution(config, `Time to fetch balances using blockscout for ${chainId}`, getIndexerBalance(config, address, chainId, isL1Chain ? tokens : undefined));
36190
36206
  }
36191
36207
  if (!web3Provider) {
36192
36208
  throw new CheckoutError('indexer is disabled for this chain, you must provide a provider.', CheckoutErrorType.MISSING_PARAMS);
@@ -37870,8 +37886,8 @@ const bridgeRoute = async (config, readOnlyProviders, availableRoutingOptions, b
37870
37886
  if (tokenBalanceResult === undefined || tokenBalanceResult.success === false)
37871
37887
  return undefined;
37872
37888
  // todo: revert this back to using the allowlist
37873
- const allowedTokenList = await allowListCheckForBridge(config, tokenBalanceResults, availableRoutingOptions);
37874
- if (allowedTokenList.length === 0)
37889
+ const allowedL1TokenList = await allowListCheckForBridge(config, tokenBalanceResults, availableRoutingOptions);
37890
+ if (allowedL1TokenList.length === 0)
37875
37891
  return undefined;
37876
37892
  const l1RepresentationResult = await fetchL1Representation(config, bridgeRequirement.l2address);
37877
37893
  if (!l1RepresentationResult)
@@ -37879,10 +37895,10 @@ const bridgeRoute = async (config, readOnlyProviders, availableRoutingOptions, b
37879
37895
  // Ensure l1address is in the allowed token list
37880
37896
  const { l1address } = l1RepresentationResult;
37881
37897
  if (isNativeToken(l1address)) {
37882
- if (!allowedTokenList.find((token) => !('address' in token)))
37898
+ if (!allowedL1TokenList.find((token) => isNativeToken(token.address)))
37883
37899
  return undefined;
37884
37900
  }
37885
- else if (!allowedTokenList.find((token) => token.address === l1address)) {
37901
+ else if (!allowedL1TokenList.find((token) => token.address === l1address)) {
37886
37902
  return undefined;
37887
37903
  }
37888
37904
  const feesFromBridge = await getBridgeFeeEstimate(config, readOnlyProviders, l1ChainId, l2ChainId);
@@ -39179,6 +39195,90 @@ function loadUnresolved(version) {
39179
39195
  return { loaded: false, element: tag };
39180
39196
  }
39181
39197
 
39198
+ const PUBLISHABLE_KEY_PREFIX = 'pk_imapik-';
39199
+ const publishableKeyDomainAllowlist = [
39200
+ 'https://checkout-api.dev.immutable.com',
39201
+ 'https://checkout-api.sandbox.immutable.com',
39202
+ 'https://checkout-api.immutable.com',
39203
+ ];
39204
+ class HttpClient {
39205
+ config;
39206
+ axiosInstance;
39207
+ constructor(config) {
39208
+ this.config = config;
39209
+ this.axiosInstance = globalAxios$1.create();
39210
+ this.setupInterceptors();
39211
+ }
39212
+ // eslint-disable-next-line class-methods-use-this
39213
+ shouldAddPublishableKey(url) {
39214
+ // Check if the request is going to a whitelisted domain
39215
+ return publishableKeyDomainAllowlist.some((domain) => url.startsWith(domain));
39216
+ }
39217
+ setupInterceptors() {
39218
+ this.axiosInstance.interceptors.request.use((config) => {
39219
+ const publishableKey = this.config?.baseConfig?.publishableKey;
39220
+ if (publishableKey && this.shouldAddPublishableKey(config.url || '')) {
39221
+ if (!publishableKey.startsWith(PUBLISHABLE_KEY_PREFIX)) {
39222
+ throw new Error('Invalid Publishable key. Create your Publishable key in Immutable developer hub.'
39223
+ + ' https://hub.immutable.com');
39224
+ }
39225
+ // Add publishable key header for whitelisted domains
39226
+ // eslint-disable-next-line no-param-reassign
39227
+ config.headers['x-immutable-publishable-key'] = publishableKey;
39228
+ }
39229
+ return config;
39230
+ }, (error) => Promise.reject(error));
39231
+ }
39232
+ // eslint-disable-next-line class-methods-use-this
39233
+ processResponse(response) {
39234
+ if (response && response?.status !== 200) {
39235
+ throw new CheckoutError(`Error: ${response.status} ${response.statusText}`, CheckoutErrorType.API_ERROR);
39236
+ }
39237
+ }
39238
+ async request(config) {
39239
+ let response;
39240
+ try {
39241
+ response = await this.axiosInstance.request(config);
39242
+ }
39243
+ catch (error) {
39244
+ throw new CheckoutError(error.message, CheckoutErrorType.API_ERROR);
39245
+ }
39246
+ this.processResponse(response);
39247
+ return response;
39248
+ }
39249
+ async get(url, config) {
39250
+ const requestConfig = {
39251
+ ...config,
39252
+ url,
39253
+ };
39254
+ return await this.request(requestConfig);
39255
+ }
39256
+ async post(url, data, config) {
39257
+ const requestConfig = {
39258
+ ...config,
39259
+ data,
39260
+ url,
39261
+ };
39262
+ return await this.request(requestConfig);
39263
+ }
39264
+ async put(url, data, config) {
39265
+ const requestConfig = {
39266
+ ...config,
39267
+ data,
39268
+ url,
39269
+ };
39270
+ return await this.request(requestConfig);
39271
+ }
39272
+ async delete(url, data, config) {
39273
+ const requestConfig = {
39274
+ ...config,
39275
+ data,
39276
+ url,
39277
+ };
39278
+ return await this.request(requestConfig);
39279
+ }
39280
+ }
39281
+
39182
39282
  const SANDBOX_CONFIGURATION = {
39183
39283
  baseConfig: {
39184
39284
  environment: Environment$1.SANDBOX,
@@ -39189,6 +39289,7 @@ const WIDGETS_SCRIPT_TIMEOUT = 100;
39189
39289
  // Checkout SDK
39190
39290
  class Checkout {
39191
39291
  readOnlyProviders;
39292
+ httpClient;
39192
39293
  config;
39193
39294
  fiatRampService;
39194
39295
  availability;
@@ -39198,7 +39299,8 @@ class Checkout {
39198
39299
  * @param {CheckoutModuleConfiguration} [config=SANDBOX_CONFIGURATION] - The configuration object for the CheckoutModule.
39199
39300
  */
39200
39301
  constructor(config = SANDBOX_CONFIGURATION) {
39201
- this.config = new CheckoutConfiguration(config);
39302
+ this.httpClient = new HttpClient(config);
39303
+ this.config = new CheckoutConfiguration(config, this.httpClient);
39202
39304
  this.fiatRampService = new FiatRampService(this.config);
39203
39305
  this.readOnlyProviders = new Map();
39204
39306
  this.availability = availabilityService(this.config.isDevelopment, this.config.isProduction);
package/dist/passport.js CHANGED
@@ -2349,7 +2349,7 @@ class MultiRollupApiClients {
2349
2349
  }
2350
2350
 
2351
2351
  // eslint-disable-next-line @typescript-eslint/naming-convention
2352
- const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.37.0' };
2352
+ const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.39.0' };
2353
2353
  const createConfig = ({ basePath, headers, }) => {
2354
2354
  if (!basePath.trim()) {
2355
2355
  throw Error('basePath can not be empty');
@@ -2405,7 +2405,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
2405
2405
  chainID,
2406
2406
  coreContractAddress,
2407
2407
  registrationContractAddress,
2408
- sdkVersion: 'ts-immutable-sdk-0.37.0',
2408
+ sdkVersion: 'ts-immutable-sdk-0.39.0',
2409
2409
  });
2410
2410
  /**
2411
2411
  * Sets `sdkVersion` at the time of build
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@imtbl/sdk",
3
3
  "description": "Immutable Unified SDK",
4
- "version": "0.37.0",
4
+ "version": "0.39.0",
5
5
  "author": "Immutable",
6
6
  "browser": "./dist/index.browser.js",
7
7
  "bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
@@ -31,11 +31,14 @@
31
31
  "enc-utils": "^3.0.0",
32
32
  "ethers": "^5.7.2",
33
33
  "https-browserify": "^1.0.0",
34
+ "i18next": "^23.7.6",
35
+ "i18next-browser-languagedetector": "^7.2.0",
34
36
  "jwt-decode": "^3.1.2",
35
37
  "magic-sdk": "^21.2.0",
36
38
  "oidc-client-ts": "^2.2.1",
37
39
  "os-browserify": "^0.3.0",
38
40
  "pako": "^2.1.0",
41
+ "react-i18next": "^13.5.0",
39
42
  "stream-browserify": "^3.0.0",
40
43
  "stream-http": "^3.2.0",
41
44
  "url": "^0.11.0"