@trezor/connect 9.5.0 → 9.5.1-beta.1

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 (48) hide show
  1. package/CHANGELOG.md +20 -8
  2. package/README.md +1 -1
  3. package/lib/api/bitcoin/BitcoinFees.d.ts +25 -0
  4. package/lib/api/bitcoin/{Fees.js → BitcoinFees.js} +8 -29
  5. package/lib/api/bitcoin/TransactionComposer.d.ts +2 -2
  6. package/lib/api/bitcoin/TransactionComposer.js +5 -5
  7. package/lib/api/bitcoin/index.d.ts +1 -1
  8. package/lib/api/bitcoin/index.js +1 -1
  9. package/lib/api/blockchainEstimateFee.js +15 -4
  10. package/lib/api/{bitcoin/Fees.d.ts → common/MiscFees.d.ts} +10 -11
  11. package/lib/api/common/MiscFees.js +46 -0
  12. package/lib/api/common/paramsValidator.js +1 -1
  13. package/lib/api/ethereum/EthereumFees.d.ts +24 -0
  14. package/lib/api/ethereum/EthereumFees.js +71 -0
  15. package/lib/api/ethereum/api/ethereumSignTypedData.d.ts +6 -0
  16. package/lib/api/ethereum/ethereumDefinitions.js +1 -3
  17. package/lib/api/getCoinInfo.d.ts +18 -0
  18. package/lib/api/solana/api/solanaComposeTransaction.d.ts +1 -2
  19. package/lib/api/solana/api/solanaComposeTransaction.js +2 -5
  20. package/lib/api/unlockPath.d.ts +1 -1
  21. package/lib/backend/Blockchain.d.ts +1 -0
  22. package/lib/core/AbstractMethod.js +1 -1
  23. package/lib/data/coinInfo.d.ts +66 -0
  24. package/lib/data/connectSettings.js +3 -0
  25. package/lib/data/defaultFeeLevels.js +1 -1
  26. package/lib/data/version.d.ts +1 -1
  27. package/lib/data/version.js +1 -1
  28. package/lib/device/Device.d.ts +3 -5
  29. package/lib/device/Device.js +25 -20
  30. package/lib/device/DeviceList.d.ts +6 -13
  31. package/lib/device/DeviceList.js +44 -174
  32. package/lib/device/TransportList.d.ts +9 -0
  33. package/lib/device/TransportList.js +50 -0
  34. package/lib/device/TransportManager.d.ts +34 -0
  35. package/lib/device/TransportManager.js +149 -0
  36. package/lib/device/checkFirmwareRevision.js +9 -5
  37. package/lib/types/api/solana/index.d.ts +1 -2
  38. package/lib/types/api/solana/index.js +1 -2
  39. package/lib/types/coinInfo.d.ts +36 -0
  40. package/lib/types/device.d.ts +6 -0
  41. package/lib/types/fees.d.ts +13 -0
  42. package/lib/types/fees.js +13 -1
  43. package/lib/types/settings.d.ts +3 -1
  44. package/lib/types/utils.d.ts +0 -1
  45. package/lib/types/utils.js +0 -3
  46. package/lib/utils/assets-browser.d.ts +4 -0
  47. package/lib/utils/assets-browser.js +11 -2
  48. package/package.json +15 -15
@@ -23,8 +23,14 @@ export declare const getBitcoinNetwork: (pathOrName: DerivationPath) => ({
23
23
  };
24
24
  decimals: number;
25
25
  defaultFees: {
26
+ maxFeePerGas?: string | undefined;
27
+ maxPriorityFeePerGas?: string | undefined;
28
+ maxWaitTimeEstimate?: number | undefined;
29
+ minWaitTimeEstimate?: number | undefined;
26
30
  feeLimit?: string | undefined;
27
31
  feePerTx?: string | undefined;
32
+ baseFeePerGas?: string | undefined;
33
+ effectiveGasPrice?: string | undefined;
28
34
  label: "normal" | "custom" | "high" | "economy" | "low";
29
35
  feePerUnit: string;
30
36
  blocks: number;
@@ -83,8 +89,14 @@ export declare const getEthereumNetwork: (pathOrNetworkSymbol: DerivationPath) =
83
89
  };
84
90
  decimals: number;
85
91
  defaultFees: {
92
+ maxFeePerGas?: string | undefined;
93
+ maxPriorityFeePerGas?: string | undefined;
94
+ maxWaitTimeEstimate?: number | undefined;
95
+ minWaitTimeEstimate?: number | undefined;
86
96
  feeLimit?: string | undefined;
87
97
  feePerTx?: string | undefined;
98
+ baseFeePerGas?: string | undefined;
99
+ effectiveGasPrice?: string | undefined;
88
100
  label: "normal" | "custom" | "high" | "economy" | "low";
89
101
  feePerUnit: string;
90
102
  blocks: number;
@@ -118,8 +130,14 @@ export declare const getMiscNetwork: (pathOrName: DerivationPath) => ({
118
130
  };
119
131
  decimals: number;
120
132
  defaultFees: {
133
+ maxFeePerGas?: string | undefined;
134
+ maxPriorityFeePerGas?: string | undefined;
135
+ maxWaitTimeEstimate?: number | undefined;
136
+ minWaitTimeEstimate?: number | undefined;
121
137
  feeLimit?: string | undefined;
122
138
  feePerTx?: string | undefined;
139
+ baseFeePerGas?: string | undefined;
140
+ effectiveGasPrice?: string | undefined;
123
141
  label: "normal" | "custom" | "high" | "economy" | "low";
124
142
  feePerUnit: string;
125
143
  blocks: number;
@@ -177,8 +195,14 @@ export declare const fixCoinInfoNetwork: (ci: BitcoinNetworkInfo, path: number[]
177
195
  };
178
196
  decimals: number;
179
197
  defaultFees: {
198
+ maxFeePerGas?: string | undefined;
199
+ maxPriorityFeePerGas?: string | undefined;
200
+ maxWaitTimeEstimate?: number | undefined;
201
+ minWaitTimeEstimate?: number | undefined;
180
202
  feeLimit?: string | undefined;
181
203
  feePerTx?: string | undefined;
204
+ baseFeePerGas?: string | undefined;
205
+ effectiveGasPrice?: string | undefined;
182
206
  label: "normal" | "custom" | "high" | "economy" | "low";
183
207
  feePerUnit: string;
184
208
  blocks: number;
@@ -237,8 +261,14 @@ export declare const getCoinInfoByHash: (hash: string, networkInfo: any) => {
237
261
  };
238
262
  decimals: number;
239
263
  defaultFees: {
264
+ maxFeePerGas?: string | undefined;
265
+ maxPriorityFeePerGas?: string | undefined;
266
+ maxWaitTimeEstimate?: number | undefined;
267
+ minWaitTimeEstimate?: number | undefined;
240
268
  feeLimit?: string | undefined;
241
269
  feePerTx?: string | undefined;
270
+ baseFeePerGas?: string | undefined;
271
+ effectiveGasPrice?: string | undefined;
242
272
  label: "normal" | "custom" | "high" | "economy" | "low";
243
273
  feePerUnit: string;
244
274
  blocks: number;
@@ -297,8 +327,14 @@ export declare const getCoinInfo: (currency: string) => ({
297
327
  };
298
328
  decimals: number;
299
329
  defaultFees: {
330
+ maxFeePerGas?: string | undefined;
331
+ maxPriorityFeePerGas?: string | undefined;
332
+ maxWaitTimeEstimate?: number | undefined;
333
+ minWaitTimeEstimate?: number | undefined;
300
334
  feeLimit?: string | undefined;
301
335
  feePerTx?: string | undefined;
336
+ baseFeePerGas?: string | undefined;
337
+ effectiveGasPrice?: string | undefined;
302
338
  label: "normal" | "custom" | "high" | "economy" | "low";
303
339
  feePerUnit: string;
304
340
  blocks: number;
@@ -356,8 +392,14 @@ export declare const getCoinInfo: (currency: string) => ({
356
392
  };
357
393
  decimals: number;
358
394
  defaultFees: {
395
+ maxFeePerGas?: string | undefined;
396
+ maxPriorityFeePerGas?: string | undefined;
397
+ maxWaitTimeEstimate?: number | undefined;
398
+ minWaitTimeEstimate?: number | undefined;
359
399
  feeLimit?: string | undefined;
360
400
  feePerTx?: string | undefined;
401
+ baseFeePerGas?: string | undefined;
402
+ effectiveGasPrice?: string | undefined;
361
403
  label: "normal" | "custom" | "high" | "economy" | "low";
362
404
  feePerUnit: string;
363
405
  blocks: number;
@@ -390,8 +432,14 @@ export declare const getCoinInfo: (currency: string) => ({
390
432
  };
391
433
  decimals: number;
392
434
  defaultFees: {
435
+ maxFeePerGas?: string | undefined;
436
+ maxPriorityFeePerGas?: string | undefined;
437
+ maxWaitTimeEstimate?: number | undefined;
438
+ minWaitTimeEstimate?: number | undefined;
393
439
  feeLimit?: string | undefined;
394
440
  feePerTx?: string | undefined;
441
+ baseFeePerGas?: string | undefined;
442
+ effectiveGasPrice?: string | undefined;
395
443
  label: "normal" | "custom" | "high" | "economy" | "low";
396
444
  feePerUnit: string;
397
445
  blocks: number;
@@ -434,8 +482,14 @@ export declare const getAllNetworks: () => (({
434
482
  };
435
483
  decimals: number;
436
484
  defaultFees: {
485
+ maxFeePerGas?: string | undefined;
486
+ maxPriorityFeePerGas?: string | undefined;
487
+ maxWaitTimeEstimate?: number | undefined;
488
+ minWaitTimeEstimate?: number | undefined;
437
489
  feeLimit?: string | undefined;
438
490
  feePerTx?: string | undefined;
491
+ baseFeePerGas?: string | undefined;
492
+ effectiveGasPrice?: string | undefined;
439
493
  label: "normal" | "custom" | "high" | "economy" | "low";
440
494
  feePerUnit: string;
441
495
  blocks: number;
@@ -493,8 +547,14 @@ export declare const getAllNetworks: () => (({
493
547
  };
494
548
  decimals: number;
495
549
  defaultFees: {
550
+ maxFeePerGas?: string | undefined;
551
+ maxPriorityFeePerGas?: string | undefined;
552
+ maxWaitTimeEstimate?: number | undefined;
553
+ minWaitTimeEstimate?: number | undefined;
496
554
  feeLimit?: string | undefined;
497
555
  feePerTx?: string | undefined;
556
+ baseFeePerGas?: string | undefined;
557
+ effectiveGasPrice?: string | undefined;
498
558
  label: "normal" | "custom" | "high" | "economy" | "low";
499
559
  feePerUnit: string;
500
560
  blocks: number;
@@ -527,8 +587,14 @@ export declare const getAllNetworks: () => (({
527
587
  };
528
588
  decimals: number;
529
589
  defaultFees: {
590
+ maxFeePerGas?: string | undefined;
591
+ maxPriorityFeePerGas?: string | undefined;
592
+ maxWaitTimeEstimate?: number | undefined;
593
+ minWaitTimeEstimate?: number | undefined;
530
594
  feeLimit?: string | undefined;
531
595
  feePerTx?: string | undefined;
596
+ baseFeePerGas?: string | undefined;
597
+ effectiveGasPrice?: string | undefined;
532
598
  label: "normal" | "custom" | "high" | "economy" | "low";
533
599
  feePerUnit: string;
534
600
  blocks: number;
@@ -124,6 +124,9 @@ const parseConnectSettings = (input = {}) => {
124
124
  if (typeof input.enableFirmwareHashCheck === 'boolean') {
125
125
  settings.enableFirmwareHashCheck = Boolean(input.enableFirmwareHashCheck);
126
126
  }
127
+ if (typeof input.npmVersion === 'string') {
128
+ settings.npmVersion = input.npmVersion;
129
+ }
127
130
  return settings;
128
131
  };
129
132
  exports.parseConnectSettings = parseConnectSettings;
@@ -29,7 +29,7 @@ const getEvmChainGweiGasPrice = (chain) => EVM_GAS_PRICE_PER_CHAIN_IN_GWEI[chain
29
29
  const getBitcoinFeeLevels = (coin) => {
30
30
  const defaultFees = coin.default_fee_b;
31
31
  const shortcut = coin.shortcut.toLowerCase();
32
- const keys = Object.keys(defaultFees);
32
+ const keys = (0, utils_1.typedObjectKeys)(defaultFees);
33
33
  const levels = keys
34
34
  .sort((levelA, levelB) => defaultFees[levelB] - defaultFees[levelA])
35
35
  .map(level => {
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "9.5.0";
1
+ export declare const VERSION = "9.5.1-beta.1";
2
2
  export declare const DEFAULT_DOMAIN: string;
3
3
  export declare const CONTENT_SCRIPT_VERSION = 1;
4
4
  export declare const DEEPLINK_VERSION = 1;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEEPLINK_VERSION = exports.CONTENT_SCRIPT_VERSION = exports.DEFAULT_DOMAIN = exports.VERSION = void 0;
4
- exports.VERSION = '9.5.0';
4
+ exports.VERSION = '9.5.1-beta.1';
5
5
  const versionN = exports.VERSION.split('.').map(s => parseInt(s, 10));
6
6
  const isBeta = exports.VERSION.includes('beta');
7
7
  exports.DEFAULT_DOMAIN = isBeta
@@ -15,6 +15,7 @@ type RunOptions = {
15
15
  skipFirmwareChecks?: boolean;
16
16
  skipLanguageChecks?: boolean;
17
17
  };
18
+ export declare const CANCEL_TIMEOUT = 1000;
18
19
  export declare const GET_FEATURES_TIMEOUT = 3000;
19
20
  export declare const GET_FEATURES_TIMEOUT_REACT_NATIVE = 20000;
20
21
  export interface DeviceEvents {
@@ -39,6 +40,7 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
39
40
  readonly transportPath: import("@trezor/transport/lib/types").PathPublic;
40
41
  private readonly transportSessionOwner;
41
42
  private readonly transportDescriptorType;
43
+ private readonly bluetoothProps;
42
44
  private session;
43
45
  private lastAcquiredHere;
44
46
  private unreadableError?;
@@ -189,11 +191,7 @@ export declare class Device extends TypedEmitter<DeviceEvents> {
189
191
  getUniquePath(): DeviceUniquePath;
190
192
  isT1(): boolean;
191
193
  hasUnexpectedMode(allow: string[], require: string[]): "ui-device_bootloader_mode" | "ui-device_not_in_bootloader_mode" | "ui-device_not_initialized" | "ui-device_seedless" | null;
192
- dispose(): Promise<import("@trezor/transport/lib/types").Success<null> | {
193
- success: false;
194
- error: "session not found" | "Network request failed" | "Wrong result type." | "device disconnected during action" | "unexpected error" | "Aborted by timeout" | "Aborted by signal" | "This transport can not be used in this environment" | "device not found" | "Unable to open device" | "wrong previous session";
195
- message?: string;
196
- } | undefined>;
194
+ dispose(): import("@trezor/transport/lib/types").AsyncResultWithTypedError<null, "session not found" | "Network request failed" | "Wrong result type." | "device disconnected during action" | "unexpected error" | "Aborted by timeout" | "Aborted by signal" | "This transport can not be used in this environment" | "device not found" | "Unable to open device" | "wrong previous session"> | undefined;
197
195
  private getMode;
198
196
  toMessageObject(): DeviceTyped;
199
197
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Device = exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = exports.GET_FEATURES_TIMEOUT = void 0;
3
+ exports.Device = exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = exports.GET_FEATURES_TIMEOUT = exports.CANCEL_TIMEOUT = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const protocol_1 = require("@trezor/protocol");
6
6
  const transport_1 = require("@trezor/transport");
@@ -20,6 +20,7 @@ const types_1 = require("../types");
20
20
  const debug_1 = require("../utils/debug");
21
21
  const deviceFeaturesUtils_1 = require("../utils/deviceFeaturesUtils");
22
22
  const _log = (0, debug_1.initLog)('Device');
23
+ exports.CANCEL_TIMEOUT = 1_000;
23
24
  exports.GET_FEATURES_TIMEOUT = 3_000;
24
25
  exports.GET_FEATURES_TIMEOUT_REACT_NATIVE = 20_000;
25
26
  const parseRunOptions = (options) => {
@@ -33,6 +34,7 @@ class Device extends utils_1.TypedEmitter {
33
34
  transportPath;
34
35
  transportSessionOwner;
35
36
  transportDescriptorType;
37
+ bluetoothProps;
36
38
  session;
37
39
  lastAcquiredHere;
38
40
  unreadableError;
@@ -89,6 +91,7 @@ class Device extends utils_1.TypedEmitter {
89
91
  this.transportPath = descriptor.path;
90
92
  this.transportSessionOwner = descriptor.sessionOwner;
91
93
  this.transportDescriptorType = descriptor.type;
94
+ this.bluetoothProps = descriptor.id ? { id: descriptor.id } : undefined;
92
95
  this.session = descriptor.session;
93
96
  this.lastAcquiredHere = false;
94
97
  this.firstRunPromise = (0, utils_1.createDeferred)();
@@ -326,16 +329,22 @@ class Device extends utils_1.TypedEmitter {
326
329
  await this.initialize(!!options.useCardanoDerivation);
327
330
  }
328
331
  else {
329
- const getFeaturesTimeout = DataManager_1.DataManager.getSettings('env') === 'react-native'
332
+ const isNative = DataManager_1.DataManager.getSettings('env') === 'react-native';
333
+ const getFeaturesTimeout = isNative
330
334
  ? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
331
335
  : exports.GET_FEATURES_TIMEOUT;
332
- if (['v1', 'bridge'].includes(this.protocol.name)) {
336
+ const cancelTimeout = isNative
337
+ ? exports.GET_FEATURES_TIMEOUT_REACT_NATIVE
338
+ : exports.CANCEL_TIMEOUT;
339
+ if (['v1', 'bridge'].includes(this.protocol.name) &&
340
+ ![0, 2].includes(this.transportDescriptorType)) {
333
341
  _log.debug('sending a preventive cancel on the first encounter with the device');
334
- try {
335
- await this.getCommands().typedCall('Cancel', 'Failure', {});
336
- }
337
- catch {
338
- }
342
+ await Promise.race([
343
+ this.getCommands()
344
+ .typedCall('Cancel', 'Failure', {})
345
+ .catch(() => { }),
346
+ new Promise((_, reject) => setTimeout(reject, cancelTimeout)),
347
+ ]).catch(() => this.acquire());
339
348
  }
340
349
  let getFeaturesTimeoutId;
341
350
  await Promise.race([
@@ -764,20 +773,14 @@ class Device extends utils_1.TypedEmitter {
764
773
  }
765
774
  return null;
766
775
  }
767
- async dispose() {
776
+ dispose() {
768
777
  this.removeAllListeners();
769
778
  if (this.session && this.lastAcquiredHere) {
770
- try {
771
- await this.cancelableAction?.();
772
- await this.commands?.cancel();
773
- return this.transport.release({
774
- session: this.session,
775
- path: this.transportPath,
776
- onClose: true,
777
- });
778
- }
779
- catch {
780
- }
779
+ return this.transport.release({
780
+ session: this.session,
781
+ path: this.transportPath,
782
+ onClose: true,
783
+ });
781
784
  }
782
785
  }
783
786
  getMode() {
@@ -808,6 +811,7 @@ class Device extends utils_1.TypedEmitter {
808
811
  label: 'Unacquired device',
809
812
  name: this.name,
810
813
  transportSessionOwner: this.transportSessionOwner,
814
+ bluetoothProps: this.bluetoothProps,
811
815
  };
812
816
  }
813
817
  const defaultLabel = 'My Trezor';
@@ -832,6 +836,7 @@ class Device extends utils_1.TypedEmitter {
832
836
  unavailableCapabilities: this.unavailableCapabilities,
833
837
  availableTranslations: this.availableTranslations,
834
838
  authenticityChecks: this.authenticityChecks,
839
+ bluetoothProps: this.bluetoothProps,
835
840
  };
836
841
  }
837
842
  }
@@ -28,13 +28,14 @@ type ConstructorParams = Pick<ConnectSettings, 'priority' | 'debug' | '_sessions
28
28
  };
29
29
  type InitParams = Pick<ConnectSettings, 'transports' | 'pendingTransportEvent' | 'transportReconnect'>;
30
30
  export declare class DeviceList extends TypedEmitter<DeviceListEvents> implements IDeviceList {
31
- private readonly transport;
31
+ private readonly transportManagers;
32
32
  private transports;
33
33
  private readonly devices;
34
34
  private deviceCounter;
35
35
  private readonly handshakeLock;
36
36
  private readonly authPenaltyManager;
37
- private transportCommonArgs;
37
+ private updateTransports;
38
+ private getConnectedTransports;
38
39
  isConnected(): this is DeviceList;
39
40
  pendingConnection(): Promise<void> | undefined;
40
41
  getActiveTransports(): {
@@ -43,22 +44,14 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
43
44
  version: string;
44
45
  outdated: boolean;
45
46
  }[];
46
- private readonly locks;
47
- private transportLock;
48
- private readonly scheduledUpgradeChecks;
49
47
  constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }: ConstructorParams);
50
- private tryGetTransport;
51
- private getOrCreateTransport;
52
- private createTransports;
53
48
  private onDeviceConnected;
54
49
  private onDeviceDisconnected;
55
50
  private onDeviceSessionChanged;
56
51
  private onDeviceRequestRelease;
57
- init(initParams?: InitParams): Promise<void>;
58
- private createInitPromise;
59
- private createReconnectDelay;
60
- private scheduleUpgradeCheck;
61
- private selectTransport;
52
+ private getOrCreateTransportManager;
53
+ init({ transports, transportReconnect, pendingTransportEvent }?: InitParams): Promise<void>;
54
+ private startTransport;
62
55
  private initializeTransport;
63
56
  private waitForDevices;
64
57
  getDeviceCount(): number;
@@ -6,9 +6,9 @@ const utils_1 = require("@trezor/utils");
6
6
  const constants_1 = require("../constants");
7
7
  const events_1 = require("../events");
8
8
  const Device_1 = require("./Device");
9
- const transportInfo_1 = require("../data/transportInfo");
10
9
  const types_1 = require("../types");
11
- const utils_2 = require("../types/utils");
10
+ const TransportList_1 = require("./TransportList");
11
+ const TransportManager_1 = require("./TransportManager");
12
12
  const debug_1 = require("../utils/debug");
13
13
  const createAuthPenaltyManager = (priority) => {
14
14
  const penalizedDevices = {};
@@ -67,100 +67,42 @@ const assertDeviceListConnected = deviceList => {
67
67
  };
68
68
  exports.assertDeviceListConnected = assertDeviceListConnected;
69
69
  class DeviceList extends utils_1.TypedEmitter {
70
- transport = {};
70
+ transportManagers = {};
71
71
  transports = [];
72
72
  devices = createDeviceCollection();
73
73
  deviceCounter = Date.now();
74
74
  handshakeLock;
75
75
  authPenaltyManager;
76
- transportCommonArgs;
76
+ updateTransports;
77
+ getConnectedTransports() {
78
+ return Object.values(this.transportManagers)
79
+ .map(manager => manager.get())
80
+ .filter(utils_1.isNotUndefined);
81
+ }
77
82
  isConnected() {
78
- return !!Object.keys(this.transport).length;
83
+ return !!this.getConnectedTransports().length;
79
84
  }
80
85
  pendingConnection() {
81
- const pending = Object.values(this.locks)
82
- .map(({ promise }) => promise)
86
+ const pending = Object.values(this.transportManagers)
87
+ .map(manager => manager.pending())
83
88
  .filter(utils_1.isNotUndefined);
84
89
  if (pending.length)
85
90
  return Promise.all(pending).then(() => { });
86
91
  }
87
92
  getActiveTransports() {
88
- return Object.values(this.transport).map(getTransportInfo);
89
- }
90
- locks = {};
91
- async transportLock(apiType, abortMessage, action) {
92
- const lock = this.locks[apiType] ?? (this.locks[apiType] = { sequence: 0 });
93
- lock.abortMessage = abortMessage;
94
- const sequence = ++lock.sequence;
95
- while (lock.promise) {
96
- lock.abort?.abort(new Error(abortMessage));
97
- await lock.promise.catch(() => { });
98
- }
99
- if (sequence !== lock.sequence)
100
- return Promise.reject(new Error(lock.abortMessage));
101
- lock.abort = new AbortController();
102
- lock.promise = action(lock.abort.signal).finally(() => {
103
- delete lock.abort;
104
- delete lock.promise;
105
- });
106
- return lock.promise;
93
+ return this.getConnectedTransports().map(getTransportInfo);
107
94
  }
108
- scheduledUpgradeChecks = {};
109
95
  constructor({ messages, priority, debug, _sessionsBackgroundUrl, manifest, }) {
110
96
  super();
111
97
  const transportLogger = (0, debug_1.initLog)('@trezor/transport', debug);
112
98
  this.handshakeLock = (0, utils_1.getSynchronize)();
113
99
  this.authPenaltyManager = createAuthPenaltyManager(priority);
114
- this.transportCommonArgs = {
100
+ this.updateTransports = (0, TransportList_1.createTransportList)({
115
101
  messages,
116
102
  logger: transportLogger,
117
103
  sessionsBackgroundUrl: _sessionsBackgroundUrl,
118
104
  id: manifest?.appUrl || 'unknown app',
119
- };
120
- }
121
- tryGetTransport(name) {
122
- return this.transports.find(t => t.name === name);
123
- }
124
- getOrCreateTransport(transportType) {
125
- const { transportCommonArgs } = this;
126
- if (typeof transportType === 'string') {
127
- const existing = this.tryGetTransport(transportType);
128
- if (existing)
129
- return existing;
130
- switch (transportType) {
131
- case 'WebUsbTransport':
132
- return new transport_1.WebUsbTransport(transportCommonArgs);
133
- case 'NodeUsbTransport':
134
- return new transport_1.NodeUsbTransport(transportCommonArgs);
135
- case 'BridgeTransport':
136
- return new transport_1.BridgeTransport({
137
- latestVersion: (0, transportInfo_1.getBridgeInfo)().version.join('.'),
138
- ...transportCommonArgs,
139
- });
140
- case 'UdpTransport':
141
- return new transport_1.UdpTransport(transportCommonArgs);
142
- }
143
- }
144
- else if (typeof transportType === 'function' && 'prototype' in transportType) {
145
- const transportInstance = new transportType(transportCommonArgs);
146
- if ((0, transport_1.isTransportInstance)(transportInstance)) {
147
- return this.tryGetTransport(transportInstance.name) ?? transportInstance;
148
- }
149
- }
150
- else if ((0, transport_1.isTransportInstance)(transportType)) {
151
- if (this.tryGetTransport(transportType.name)) {
152
- return transportType;
153
- }
154
- if (!transportType.getMessage()) {
155
- transportType.updateMessages(transportCommonArgs.messages);
156
- }
157
- return transportType;
158
- }
159
- throw constants_1.ERRORS.TypedError('Runtime', `DeviceList.init: transports[] of unexpected type: ${transportType}`);
160
- }
161
- createTransports(transports) {
162
- const transportTypes = transports?.length ? transports : ['BridgeTransport'];
163
- return transportTypes.map(this.getOrCreateTransport.bind(this));
105
+ });
164
106
  }
165
107
  onDeviceConnected(descriptor, transport) {
166
108
  const id = (this.deviceCounter++).toString(16).slice(-8);
@@ -190,103 +132,41 @@ class DeviceList extends utils_1.TypedEmitter {
190
132
  const device = this.devices.get(descriptor.path, transport);
191
133
  device?.usedElsewhere();
192
134
  }
193
- async init(initParams = {}) {
194
- this.transports = this.createTransports(initParams.transports);
135
+ getOrCreateTransportManager(apiType) {
136
+ if (!this.transportManagers[apiType]) {
137
+ const manager = new TransportManager_1.TransportManager({
138
+ startTransport: this.startTransport.bind(this),
139
+ stopTransport: this.stopTransport.bind(this),
140
+ });
141
+ manager.on(transport_1.TRANSPORT.START, transport => this.emit(transport_1.TRANSPORT.START, getTransportInfo(transport)));
142
+ manager.on(transport_1.TRANSPORT.ERROR, error => this.emit(transport_1.TRANSPORT.ERROR, { apiType, error }));
143
+ this.transportManagers[apiType] = manager;
144
+ }
145
+ return this.transportManagers[apiType];
146
+ }
147
+ async init({ transports, transportReconnect, pendingTransportEvent } = {}) {
148
+ this.transports = this.updateTransports(this.transports, transports);
195
149
  const promises = this.transports
196
150
  .map(t => t.apiType)
197
- .concat((0, utils_2.typedObjectKeys)(this.transport))
198
- .concat((0, utils_2.typedObjectKeys)(this.locks))
151
+ .concat((0, utils_1.typedObjectKeys)(this.transportManagers))
199
152
  .filter(utils_1.arrayDistinct)
200
- .map(apiType => this.transportLock(apiType, 'New init', signal => this.createInitPromise(apiType, initParams, signal)));
153
+ .map(apiType => this.getOrCreateTransportManager(apiType).init({
154
+ transports: this.transports.filter(t => t.apiType === apiType),
155
+ transportReconnect,
156
+ pendingTransportEvent,
157
+ }));
201
158
  await Promise.all(promises);
202
159
  }
203
- async createInitPromise(apiType, initParams, abortSignal) {
160
+ async startTransport(transport, pendingTransportEvent, signal) {
204
161
  try {
205
- const transports = this.transports.filter(t => t.apiType === apiType);
206
- const transport = transports.length
207
- ? await this.selectTransport(transports, abortSignal)
208
- : undefined;
209
- const oldTransport = this.transport[apiType];
210
- if (oldTransport !== transport) {
211
- if (oldTransport) {
212
- delete this.transport[apiType];
213
- await this.stopTransport(oldTransport);
214
- if (!transport) {
215
- this.emit(transport_1.TRANSPORT.ERROR, { apiType, error: 'Transport disabled' });
216
- }
217
- }
218
- if (transport) {
219
- try {
220
- await this.initializeTransport(transport, initParams, abortSignal);
221
- }
222
- catch (error) {
223
- await this.stopTransport(transport);
224
- throw error;
225
- }
226
- transport.on(transport_1.TRANSPORT.ERROR, error => {
227
- this.emit(transport_1.TRANSPORT.ERROR, { apiType, error });
228
- this.transportLock(apiType, 'Transport error', async (signal) => {
229
- delete this.transport[apiType];
230
- await this.stopTransport(transport);
231
- if (initParams.transportReconnect) {
232
- await this.createReconnectDelay(signal);
233
- await this.createInitPromise(apiType, initParams, signal);
234
- }
235
- }).catch(() => { });
236
- });
237
- this.transport[apiType] = transport;
238
- this.emit(transport_1.TRANSPORT.START, getTransportInfo(transport));
239
- }
240
- }
241
- if (transport && transport !== transports[0]) {
242
- this.scheduleUpgradeCheck(apiType, initParams);
243
- }
162
+ await this.initializeTransport(transport, pendingTransportEvent, signal);
244
163
  }
245
- catch (error) {
246
- this.emit(transport_1.TRANSPORT.ERROR, { apiType, error: error?.message });
247
- if (initParams.transportReconnect && !abortSignal.aborted) {
248
- this.transportLock(apiType, 'Reconnecting', async (signal) => {
249
- await this.createReconnectDelay(signal);
250
- await this.createInitPromise(apiType, initParams, signal);
251
- }).catch(() => { });
252
- }
164
+ catch (err) {
165
+ await this.stopTransport(transport);
166
+ throw err;
253
167
  }
254
168
  }
255
- createReconnectDelay(signal) {
256
- return (0, utils_1.resolveAfter)(1000, signal);
257
- }
258
- scheduleUpgradeCheck(apiType, initParams) {
259
- clearTimeout(this.scheduledUpgradeChecks[apiType]);
260
- this.scheduledUpgradeChecks[apiType] = setTimeout(async () => {
261
- const transport = this.transport[apiType];
262
- const transports = this.transports.filter(t => t.apiType === apiType);
263
- if (!transport || transport === transports[0])
264
- return;
265
- for (const t of transports) {
266
- if (t === transport)
267
- break;
268
- if (await t.ping()) {
269
- this.transportLock(apiType, 'Upgrading', signal => this.createInitPromise(apiType, initParams, signal)).catch(() => { });
270
- return;
271
- }
272
- }
273
- this.scheduleUpgradeCheck(apiType, initParams);
274
- }, 1000);
275
- }
276
- async selectTransport([transport, ...rest], signal) {
277
- if (signal.aborted)
278
- throw new Error(signal.reason);
279
- if (transport === this.transport[transport.apiType])
280
- return transport;
281
- const result = await transport.init({ signal });
282
- if (result.success)
283
- return transport;
284
- else if (rest.length)
285
- return this.selectTransport(rest, signal);
286
- else
287
- throw new Error(result.error);
288
- }
289
- async initializeTransport(transport, initParams, signal) {
169
+ async initializeTransport(transport, pendingTransportEvent, signal) {
290
170
  transport.on(transport_1.TRANSPORT.DEVICE_CONNECTED, d => this.onDeviceConnected(d, transport));
291
171
  transport.on(transport_1.TRANSPORT.DEVICE_DISCONNECTED, d => this.onDeviceDisconnected(d, transport));
292
172
  transport.on(transport_1.TRANSPORT.DEVICE_SESSION_CHANGED, d => this.onDeviceSessionChanged(d, transport));
@@ -296,7 +176,7 @@ class DeviceList extends utils_1.TypedEmitter {
296
176
  throw new Error(enumerateResult.error);
297
177
  }
298
178
  const descriptors = enumerateResult.payload;
299
- const waitForDevicesPromise = initParams.pendingTransportEvent && descriptors.length
179
+ const waitForDevicesPromise = pendingTransportEvent && descriptors.length
300
180
  ? this.waitForDevices(transport, descriptors, signal)
301
181
  : Promise.resolve();
302
182
  transport.handleDescriptorsChange(descriptors);
@@ -348,20 +228,10 @@ class DeviceList extends utils_1.TypedEmitter {
348
228
  }
349
229
  async dispose() {
350
230
  this.removeAllListeners();
351
- const promises = (0, utils_2.typedObjectKeys)(this.transport)
352
- .concat((0, utils_2.typedObjectKeys)(this.locks))
353
- .filter(utils_1.arrayDistinct)
354
- .map(apiType => this.transportLock(apiType, 'Disposing', async () => {
355
- const transport = this.transport[apiType];
356
- if (transport) {
357
- delete this.transport[apiType];
358
- await this.stopTransport(transport);
359
- }
360
- }));
231
+ const promises = Object.values(this.transportManagers).map(manager => manager.dispose());
361
232
  await Promise.all(promises);
362
233
  }
363
234
  async stopTransport(transport) {
364
- clearTimeout(this.scheduledUpgradeChecks[transport.apiType]);
365
235
  const devices = this.devices.clear(transport);
366
236
  devices.forEach(device => {
367
237
  this.emit(events_1.DEVICE.DISCONNECT, device);
@@ -373,7 +243,7 @@ class DeviceList extends utils_1.TypedEmitter {
373
243
  transport?.stop();
374
244
  }
375
245
  async enumerate() {
376
- const promises = Object.values(this.transport).map(async (transport) => {
246
+ const promises = this.getConnectedTransports().map(async (transport) => {
377
247
  const res = await transport.enumerate();
378
248
  if (!res.success) {
379
249
  return;