@subwallet/extension-base 1.1.22-0 → 1.1.24-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.
Files changed (58) hide show
  1. package/background/KoniTypes.d.ts +21 -2
  2. package/cjs/koni/api/dotsama/balance.js +49 -0
  3. package/cjs/koni/api/dotsama/crowdloan.js +29 -3
  4. package/cjs/koni/api/dotsama/transfer.js +10 -3
  5. package/cjs/koni/api/staking/bonding/astar.js +9 -5
  6. package/cjs/koni/api/xcm/index.js +6 -1
  7. package/cjs/koni/api/xcm/polkadotXcm.js +1 -3
  8. package/cjs/koni/api/xcm/xTokens.js +1 -1
  9. package/cjs/koni/background/handlers/Extension.js +123 -96
  10. package/cjs/koni/background/handlers/Mobile.js +32 -0
  11. package/cjs/koni/background/handlers/State.js +15 -7
  12. package/cjs/koni/background/subscription.js +1 -1
  13. package/cjs/packageInfo.js +1 -1
  14. package/cjs/services/chain-service/constants.js +5 -2
  15. package/cjs/services/chain-service/index.js +18 -0
  16. package/cjs/services/history-service/helpers/subscan-extrinsic-parser-helper.js +53 -0
  17. package/cjs/services/history-service/index.js +78 -21
  18. package/cjs/services/history-service/subscan-history.js +107 -0
  19. package/cjs/services/storage-service/DatabaseService.js +40 -0
  20. package/cjs/services/subscan-service/index.js +109 -4
  21. package/cjs/services/subscan-service/subscan-chain-map.js +82 -8
  22. package/cjs/utils/index.js +10 -1
  23. package/koni/api/dotsama/balance.js +49 -0
  24. package/koni/api/dotsama/crowdloan.d.ts +2 -2
  25. package/koni/api/dotsama/crowdloan.js +29 -2
  26. package/koni/api/dotsama/transfer.js +10 -3
  27. package/koni/api/staking/bonding/astar.js +9 -5
  28. package/koni/api/xcm/index.js +6 -1
  29. package/koni/api/xcm/polkadotXcm.js +2 -4
  30. package/koni/api/xcm/xTokens.js +1 -1
  31. package/koni/background/handlers/Extension.d.ts +1 -0
  32. package/koni/background/handlers/Extension.js +26 -0
  33. package/koni/background/handlers/Mobile.d.ts +5 -1
  34. package/koni/background/handlers/Mobile.js +31 -0
  35. package/koni/background/handlers/State.d.ts +1 -0
  36. package/koni/background/handlers/State.js +17 -9
  37. package/koni/background/subscription.js +1 -1
  38. package/package.json +17 -6
  39. package/packageInfo.js +1 -1
  40. package/services/chain-service/constants.d.ts +2 -0
  41. package/services/chain-service/constants.js +5 -3
  42. package/services/chain-service/index.d.ts +1 -0
  43. package/services/chain-service/index.js +18 -0
  44. package/services/history-service/helpers/subscan-extrinsic-parser-helper.d.ts +6 -0
  45. package/services/history-service/helpers/subscan-extrinsic-parser-helper.js +44 -0
  46. package/services/history-service/index.d.ts +10 -6
  47. package/services/history-service/index.js +79 -22
  48. package/services/history-service/subscan-history.d.ts +5 -0
  49. package/services/history-service/subscan-history.js +100 -0
  50. package/services/storage-service/DatabaseService.d.ts +3 -0
  51. package/services/storage-service/DatabaseService.js +40 -0
  52. package/services/subscan-service/index.d.ts +10 -2
  53. package/services/subscan-service/index.js +105 -4
  54. package/services/subscan-service/subscan-chain-map.d.ts +9 -3
  55. package/services/subscan-service/subscan-chain-map.js +78 -5
  56. package/services/subscan-service/types.d.ts +146 -0
  57. package/utils/index.d.ts +1 -0
  58. package/utils/index.js +7 -0
@@ -83,4 +83,7 @@ export default class DatabaseService {
83
83
  getProcessingCampaign(): Promise<import("@subwallet/extension-base/background/KoniTypes").CampaignData[]>;
84
84
  getCampaign(slug: string): Promise<import("@subwallet/extension-base/background/KoniTypes").CampaignData | undefined>;
85
85
  upsertCampaign(campaign: ICampaign): import("dexie").PromiseExtended<unknown>;
86
+ exportDB(): Promise<string>;
87
+ importDB(data: string): Promise<boolean>;
88
+ checkImportMetadata(data: string): Promise<import("dexie-export-import").DexieExportJsonMeta | null>;
86
89
  }
@@ -9,7 +9,9 @@ import ChainStakingMetadataStore from '@subwallet/extension-base/services/storag
9
9
  import MantaPayStore from '@subwallet/extension-base/services/storage-service/db-stores/MantaPay';
10
10
  import NominatorMetadataStore from '@subwallet/extension-base/services/storage-service/db-stores/NominatorMetadata';
11
11
  import { reformatAddress } from '@subwallet/extension-base/utils';
12
+ import { exportDB, peakImportFile } from 'dexie-export-import';
12
13
  import { logger as createLogger } from '@polkadot/util';
14
+ const EXPORT_EXCLUDE_TABLES = ['metadata'];
13
15
  export default class DatabaseService {
14
16
  // TODO: might remove this
15
17
 
@@ -279,4 +281,42 @@ export default class DatabaseService {
279
281
  upsertCampaign(campaign) {
280
282
  return this.stores.campaign.upsertCampaign(campaign);
281
283
  }
284
+ async exportDB() {
285
+ const blob = await exportDB(this._db, {
286
+ filter: (table, value, key) => {
287
+ if (EXPORT_EXCLUDE_TABLES.indexOf(table) >= 0) {
288
+ return false;
289
+ }
290
+ return true;
291
+ }
292
+ });
293
+ return await blob.text();
294
+ }
295
+ async importDB(data) {
296
+ try {
297
+ const blob = new Blob([data], {
298
+ type: 'application/json'
299
+ });
300
+ await this._db.import(blob, {
301
+ overwriteValues: true,
302
+ acceptMissingTables: true,
303
+ acceptVersionDiff: true
304
+ });
305
+ return true;
306
+ } catch (e) {
307
+ this.logger.error(e);
308
+ return false;
309
+ }
310
+ }
311
+ async checkImportMetadata(data) {
312
+ try {
313
+ const blob = new Blob([data], {
314
+ type: 'application/json'
315
+ });
316
+ return await peakImportFile(blob);
317
+ } catch (e) {
318
+ this.logger.error(e);
319
+ return null;
320
+ }
321
+ }
282
322
  }
@@ -1,5 +1,6 @@
1
- import { IMultiChainBalance } from '@subwallet/extension-base/services/subscan-service/types';
1
+ import { CrowdloanContributionsResponse, ExtrinsicItem, ExtrinsicsListResponse, IMultiChainBalance, TransferItem, TransfersListResponse } from '@subwallet/extension-base/services/subscan-service/types';
2
2
  export declare class SubscanService {
3
+ private subscanChainMap;
3
4
  private limitRate;
4
5
  private intervalCheck;
5
6
  private maxRetry;
@@ -7,7 +8,7 @@ export declare class SubscanService {
7
8
  private nextId;
8
9
  private isRunning;
9
10
  private getId;
10
- constructor(options?: {
11
+ constructor(subscanChainMap: Record<string, string>, options?: {
11
12
  limitRate?: number;
12
13
  intervalCheck?: number;
13
14
  maxRetry?: number;
@@ -16,5 +17,12 @@ export declare class SubscanService {
16
17
  private postRequest;
17
18
  private addRequest;
18
19
  private process;
20
+ checkSupportedSubscanChain(chain: string): boolean;
21
+ setSubscanChainMap(subscanChainMap: Record<string, string>): void;
19
22
  getMultiChainBalance(address: string): Promise<IMultiChainBalance[]>;
23
+ getCrowdloanContributions(relayChain: string, address: string, page?: number): Promise<CrowdloanContributionsResponse>;
24
+ getExtrinsicsList(chain: string, address: string, page?: number): Promise<ExtrinsicsListResponse>;
25
+ fetchAllPossibleExtrinsicItems(chain: string, address: string, cbAfterEachRequest?: (items: ExtrinsicItem[]) => void): Promise<ExtrinsicItem[]>;
26
+ getTransfersList(chain: string, address: string, page?: number, direction?: 'sent' | 'received'): Promise<TransfersListResponse>;
27
+ fetchAllPossibleTransferItems(chain: string, address: string, direction?: 'sent' | 'received', cbAfterEachRequest?: (items: TransferItem[]) => void): Promise<TransferItem[]>;
20
28
  }
@@ -2,8 +2,9 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { SWError } from '@subwallet/extension-base/background/errors/SWError';
5
- import SUBSCAN_CHAIN_MAP from '@subwallet/extension-base/services/subscan-service/subscan-chain-map';
5
+ import { wait } from '@subwallet/extension-base/utils';
6
6
  import fetch from 'cross-fetch';
7
+ const QUERY_ROW = 100;
7
8
  export class SubscanService {
8
9
  limitRate = 2; // limit per interval check
9
10
  intervalCheck = 1000; // interval check in ms
@@ -14,17 +15,18 @@ export class SubscanService {
14
15
  getId() {
15
16
  return this.nextId++;
16
17
  }
17
- constructor(options) {
18
+ constructor(subscanChainMap, options) {
19
+ this.subscanChainMap = subscanChainMap;
18
20
  this.limitRate = (options === null || options === void 0 ? void 0 : options.limitRate) || this.limitRate;
19
21
  this.intervalCheck = (options === null || options === void 0 ? void 0 : options.intervalCheck) || this.intervalCheck;
20
22
  this.maxRetry = (options === null || options === void 0 ? void 0 : options.maxRetry) || this.maxRetry;
21
23
  }
22
24
  getApiUrl(chain, path) {
23
- const subscanChain = SUBSCAN_CHAIN_MAP[chain];
25
+ const subscanChain = this.subscanChainMap[chain];
24
26
  if (!subscanChain) {
25
27
  throw new SWError('NOT_SUPPORTED', 'Chain is not supported');
26
28
  }
27
- return `https://${chain}.api.subscan.io/${path}`;
29
+ return `https://${subscanChain}.api.subscan.io/${path}`;
28
30
  }
29
31
  postRequest(url, body) {
30
32
  return fetch(url, {
@@ -82,6 +84,12 @@ export class SubscanService {
82
84
  });
83
85
  }, this.intervalCheck);
84
86
  }
87
+ checkSupportedSubscanChain(chain) {
88
+ return !!this.subscanChainMap[chain];
89
+ }
90
+ setSubscanChainMap(subscanChainMap) {
91
+ this.subscanChainMap = subscanChainMap;
92
+ }
85
93
 
86
94
  // Implement Subscan API
87
95
  getMultiChainBalance(address) {
@@ -96,4 +104,97 @@ export class SubscanService {
96
104
  return jsonData.data;
97
105
  });
98
106
  }
107
+ getCrowdloanContributions(relayChain, address, page = 0) {
108
+ return this.addRequest(async () => {
109
+ const rs = await this.postRequest(this.getApiUrl(relayChain, 'api/scan/account/contributions'), {
110
+ include_total: true,
111
+ page,
112
+ row: QUERY_ROW,
113
+ who: address
114
+ });
115
+ if (rs.status !== 200) {
116
+ throw new SWError('SubscanService.getCrowdloanContributions', await rs.text());
117
+ }
118
+ const jsonData = await rs.json();
119
+ return jsonData.data;
120
+ });
121
+ }
122
+ getExtrinsicsList(chain, address, page = 0) {
123
+ return this.addRequest(async () => {
124
+ const rs = await this.postRequest(this.getApiUrl(chain, 'api/scan/extrinsics'), {
125
+ page,
126
+ row: QUERY_ROW,
127
+ address
128
+ });
129
+ if (rs.status !== 200) {
130
+ throw new SWError('SubscanService.getExtrinsicsList', await rs.text());
131
+ }
132
+ const jsonData = await rs.json();
133
+ return jsonData.data;
134
+ });
135
+ }
136
+ async fetchAllPossibleExtrinsicItems(chain, address, cbAfterEachRequest) {
137
+ let count = 0;
138
+ const resultMap = {};
139
+ const _getExtrinsicItems = async page => {
140
+ const res = await this.getExtrinsicsList(chain, address, page);
141
+ if (!res || !res.count || !res.extrinsics || !res.extrinsics.length) {
142
+ return;
143
+ }
144
+ if (res.count > count) {
145
+ count = res.count;
146
+ }
147
+ cbAfterEachRequest === null || cbAfterEachRequest === void 0 ? void 0 : cbAfterEachRequest(res.extrinsics);
148
+ res.extrinsics.forEach(item => {
149
+ resultMap[item.extrinsic_hash] = item;
150
+ });
151
+ if (Object.values(resultMap).length < count) {
152
+ await wait(100);
153
+ await _getExtrinsicItems(++page);
154
+ }
155
+ };
156
+ await _getExtrinsicItems(0);
157
+ return Object.values(resultMap);
158
+ }
159
+ getTransfersList(chain, address, page = 0, direction) {
160
+ const requestBody = {
161
+ page,
162
+ row: QUERY_ROW,
163
+ address
164
+ };
165
+ if (direction) {
166
+ requestBody.direction = direction;
167
+ }
168
+ return this.addRequest(async () => {
169
+ const rs = await this.postRequest(this.getApiUrl(chain, 'api/v2/scan/transfers'), requestBody);
170
+ if (rs.status !== 200) {
171
+ throw new SWError('SubscanService.getTransfersList', await rs.text());
172
+ }
173
+ const jsonData = await rs.json();
174
+ return jsonData.data;
175
+ });
176
+ }
177
+ async fetchAllPossibleTransferItems(chain, address, direction, cbAfterEachRequest) {
178
+ let count = 0;
179
+ const resultMap = {};
180
+ const _getTransferItems = async page => {
181
+ const res = await this.getTransfersList(chain, address, page, direction);
182
+ if (!res || !res.count || !res.transfers || !res.transfers.length) {
183
+ return;
184
+ }
185
+ if (res.count > count) {
186
+ count = res.count;
187
+ }
188
+ cbAfterEachRequest === null || cbAfterEachRequest === void 0 ? void 0 : cbAfterEachRequest(res.transfers);
189
+ res.transfers.forEach(item => {
190
+ resultMap[item.hash] = item;
191
+ });
192
+ if (Object.values(resultMap).length < count) {
193
+ await wait(100);
194
+ await _getTransferItems(++page);
195
+ }
196
+ };
197
+ await _getTransferItems(0);
198
+ return Object.values(resultMap);
199
+ }
99
200
  }
@@ -1,5 +1,11 @@
1
- declare const SUBSCAN_CHAIN_MAP: Record<string, string>;
2
- export declare const SUBSCAN_CHAIN_MAP_REVERSE: {
1
+ /**
2
+ * Only use this for initiating SubscanService or unit test
3
+ */
4
+ export declare const SUBSCAN_API_CHAIN_MAP: Record<string, string>;
5
+ /**
6
+ * Map for token self-activation feature
7
+ */
8
+ export declare const SUBSCAN_BALANCE_CHAIN_MAP: Record<string, string>;
9
+ export declare const SUBSCAN_BALANCE_CHAIN_MAP_REVERSE: {
3
10
  [k: string]: string;
4
11
  };
5
- export default SUBSCAN_CHAIN_MAP;
@@ -1,7 +1,76 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- const SUBSCAN_CHAIN_MAP = {
4
+ /**
5
+ * Only use this for initiating SubscanService or unit test
6
+ */
7
+ export const SUBSCAN_API_CHAIN_MAP = {
8
+ polkadot: 'polkadot',
9
+ kusama: 'kusama',
10
+ moonbeam: 'moonbeam',
11
+ pioneer: 'pioneer',
12
+ aleph: 'alephzero',
13
+ astar: 'astar',
14
+ statemint: 'assethub-polkadot',
15
+ acala: 'acala',
16
+ shiden: 'shiden',
17
+ shibuya: 'shibuya',
18
+ westend: 'westend',
19
+ rococo: 'rococo',
20
+ moonbase: 'moonbase',
21
+ moonriver: 'moonriver',
22
+ turing: 'turing',
23
+ bifrost: 'bifrost-kusama',
24
+ bifrost_dot: 'bifrost',
25
+ calamari: 'calamari',
26
+ parallel: 'parallel',
27
+ clover: 'clv',
28
+ hydradx_main: 'hydradx',
29
+ centrifuge: 'centrifuge',
30
+ interlay: 'interlay',
31
+ nodle: 'nodle',
32
+ darwinia2: 'darwinia',
33
+ polkadex: 'polkadex-parachain',
34
+ composableFinance: 'composable',
35
+ phala: 'phala',
36
+ crust: 'crust-parachain',
37
+ statemine: 'assethub-kusama',
38
+ karura: 'karura',
39
+ khala: 'khala',
40
+ kilt: 'spiritnet',
41
+ basilisk: 'basilisk',
42
+ altair: 'altair',
43
+ heiko: 'parallel-heiko',
44
+ kintsugi: 'kintsugi',
45
+ picasso: 'picasso',
46
+ unique_network: 'unique',
47
+ zeitgeist: 'zeitgeist',
48
+ sakura: 'sakura',
49
+ shadow: 'shadow',
50
+ robonomics: 'robonomics',
51
+ integritee: 'integritee',
52
+ crabParachain: 'crab',
53
+ acala_testnet: 'acala-testnet',
54
+ mangatax_para: 'mangatax',
55
+ origintrail: 'origintrail',
56
+ subspace_gemini_3g: 'subspace',
57
+ bajun: 'bajun',
58
+ tanganika: 'datahighway',
59
+ kilt_peregrine: 'kilt-testnet',
60
+ dockPosMainnet: 'dock',
61
+ polymesh: 'polymesh',
62
+ sora_substrate: 'sora',
63
+ joystream: 'joystream',
64
+ vara_network: 'vara',
65
+ krest_network: 'krest',
66
+ crust_mainnet: 'crust',
67
+ manta_network: 'manta'
68
+ };
69
+
70
+ /**
71
+ * Map for token self-activation feature
72
+ */
73
+ export const SUBSCAN_BALANCE_CHAIN_MAP = {
5
74
  polkadot: 'polkadot',
6
75
  kusama: 'kusama',
7
76
  moonbeam: 'moonbeam',
@@ -21,7 +90,7 @@ const SUBSCAN_CHAIN_MAP = {
21
90
  clover: 'clover',
22
91
  hydradx_main: 'hydradx',
23
92
  edgeware: 'edgeware',
24
- centrifuge: 'centrifuge',
93
+ centrifuge: 'centrifuge-parachain',
25
94
  interlay: 'interlay',
26
95
  nodle: 'nodle',
27
96
  darwinia: 'darwinia',
@@ -57,7 +126,11 @@ const SUBSCAN_CHAIN_MAP = {
57
126
  bajun: 'bajun',
58
127
  snow: 'snow',
59
128
  kilt_peregrine: 'kilt-testnet',
60
- polymesh: 'polymesh'
129
+ polymesh: 'polymesh',
130
+ bifrost_dot: 'bifrost-p',
131
+ vara_network: 'vara',
132
+ bifrost: 'bifrost',
133
+ creditcoin: 'creditcoin',
134
+ joystream: 'joystream'
61
135
  };
62
- export const SUBSCAN_CHAIN_MAP_REVERSE = Object.fromEntries(Object.entries(SUBSCAN_CHAIN_MAP).map(([k, v]) => [v, k]));
63
- export default SUBSCAN_CHAIN_MAP;
136
+ export const SUBSCAN_BALANCE_CHAIN_MAP_REVERSE = Object.fromEntries(Object.entries(SUBSCAN_BALANCE_CHAIN_MAP).map(([k, v]) => [v, k]));
@@ -25,3 +25,149 @@ export interface IMultiChainBalance {
25
25
  democracy_lock: string;
26
26
  election_lock: string;
27
27
  }
28
+ export interface CrowdloanContributionItem {
29
+ fund_id: string;
30
+ para_id: number;
31
+ contributed: string;
32
+ block_num: number;
33
+ block_timestamp: number;
34
+ extrinsic_index: string;
35
+ event_index: string;
36
+ status: number;
37
+ memo: string;
38
+ fund_status: number;
39
+ fund_event_index: string;
40
+ unlocking_block: number;
41
+ fund_auction_status: number;
42
+ }
43
+ export interface CrowdloanContributionsResponse {
44
+ count: number;
45
+ list: null | CrowdloanContributionItem[];
46
+ total: string;
47
+ }
48
+ export interface ExtrinsicItem {
49
+ id?: number;
50
+ block_num: number;
51
+ block_timestamp: number;
52
+ extrinsic_index: string;
53
+ call_module_function: string;
54
+ params: string;
55
+ account_id: string;
56
+ account_index: string;
57
+ signature: string;
58
+ call_module: string;
59
+ nonce: number;
60
+ extrinsic_hash: string;
61
+ success: boolean;
62
+ fee: string;
63
+ fee_used: string;
64
+ from_hex: string;
65
+ tip: string;
66
+ finalized: boolean;
67
+ account_display: {
68
+ address: string;
69
+ };
70
+ }
71
+ export interface ExtrinsicsListResponse {
72
+ count: number;
73
+ extrinsics: null | ExtrinsicItem[];
74
+ }
75
+ export interface ExtrinsicParam {
76
+ name: string;
77
+ type: string;
78
+ type_name: string;
79
+ value: any;
80
+ }
81
+ export interface ExtrinsicDetailEvent {
82
+ event_index: string;
83
+ block_num: number;
84
+ extrinsic_idx: number;
85
+ module_id: string;
86
+ event_id: string;
87
+ params: string;
88
+ phase: number;
89
+ event_idx: number;
90
+ extrinsic_hash: string;
91
+ finalized: boolean;
92
+ block_timestamp: number;
93
+ }
94
+ export interface ExtrinsicDetailError {
95
+ module: string;
96
+ name: string;
97
+ doc: string;
98
+ value: string;
99
+ batch_index: number;
100
+ }
101
+ export interface ExtrinsicDetail {
102
+ block_timestamp: number;
103
+ block_num: number;
104
+ extrinsic_index: string;
105
+ call_module_function: string;
106
+ call_module: string;
107
+ account_id: string;
108
+ signature: string;
109
+ nonce: number;
110
+ extrinsic_hash: string;
111
+ success: boolean;
112
+ params: ExtrinsicParam[];
113
+ transfer: {
114
+ from: string;
115
+ to: string;
116
+ module: string;
117
+ amount: string;
118
+ hash: string;
119
+ success: boolean;
120
+ asset_symbol: string;
121
+ to_account_display: {
122
+ address: string;
123
+ };
124
+ };
125
+ event: ExtrinsicDetailEvent[];
126
+ event_count: number;
127
+ fee: string;
128
+ fee_used: string;
129
+ error: null | ExtrinsicDetailError;
130
+ finalized: boolean;
131
+ lifetime: {
132
+ birth: number;
133
+ death: number;
134
+ };
135
+ tip: string;
136
+ account_display: {
137
+ address: string;
138
+ };
139
+ block_hash: string;
140
+ pending: boolean;
141
+ }
142
+ export interface TransferItem {
143
+ from: string;
144
+ to: string;
145
+ extrinsic_index: string;
146
+ success: boolean;
147
+ hash: string;
148
+ block_num: number;
149
+ block_timestamp: number;
150
+ module: string;
151
+ amount: string;
152
+ amount_v2: string;
153
+ usd_amount: string;
154
+ fee: string;
155
+ nonce: number;
156
+ asset_symbol: string;
157
+ asset_unique_id: string;
158
+ asset_type: string;
159
+ item_id: number | null;
160
+ from_account_display: {
161
+ address: string;
162
+ display?: string;
163
+ };
164
+ to_account_display: {
165
+ address: string;
166
+ display?: string;
167
+ };
168
+ event_idx: 0;
169
+ }
170
+ export interface TransfersListResponse {
171
+ count: number;
172
+ transfers: null | TransferItem[];
173
+ }
package/utils/index.d.ts CHANGED
@@ -39,6 +39,7 @@ export declare function isSameAddress(address1: string, address2: string): boole
39
39
  export declare function getDomainFromUrl(url: string): string;
40
40
  export declare function waitTimeout(ms: number): Promise<void>;
41
41
  export declare const stripUrl: (url: string) => string;
42
+ export declare function wait(milliseconds: number): Promise<void>;
42
43
  export * from './array';
43
44
  export * from './environment';
44
45
  export * from './lazy';
package/utils/index.js CHANGED
@@ -287,6 +287,13 @@ export const stripUrl = url => {
287
287
  const parts = url.split('/');
288
288
  return parts[2];
289
289
  };
290
+ export function wait(milliseconds) {
291
+ return new Promise(resolve => {
292
+ setTimeout(() => {
293
+ resolve();
294
+ }, milliseconds);
295
+ });
296
+ }
290
297
  export * from "./array.js";
291
298
  export * from "./environment.js";
292
299
  export * from "./lazy.js";