@onekeyfe/hd-transport 1.2.0-alpha.99 → 1.2.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/README.md +1 -1
- package/__tests__/messages.test.js +25 -0
- package/__tests__/protocol-v2-link-manager.test.js +126 -0
- package/__tests__/protocol-v2-usb-transport-base.test.js +6 -4
- package/__tests__/protocol-v2.test.js +309 -27
- package/dist/index.d.ts +131 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -15
- package/dist/protocols/index.d.ts +12 -0
- package/dist/protocols/index.d.ts.map +1 -1
- package/dist/protocols/v2/errors.d.ts +8 -0
- package/dist/protocols/v2/errors.d.ts.map +1 -1
- package/dist/protocols/v2/link-manager.d.ts +2 -0
- package/dist/protocols/v2/link-manager.d.ts.map +1 -1
- package/dist/protocols/v2/session.d.ts +13 -1
- package/dist/protocols/v2/session.d.ts.map +1 -1
- package/dist/protocols/v2/usb-transport-base.d.ts +1 -0
- package/dist/protocols/v2/usb-transport-base.d.ts.map +1 -1
- package/dist/types/messages.d.ts +50 -7
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/transport.d.ts +4 -0
- package/dist/types/transport.d.ts.map +1 -1
- package/messages-protocol-v2.json +140 -13
- package/package.json +2 -2
- package/src/protocols/index.ts +91 -0
- package/src/protocols/v2/errors.ts +29 -0
- package/src/protocols/v2/link-manager.ts +57 -5
- package/src/protocols/v2/session.ts +133 -4
- package/src/protocols/v2/usb-transport-base.ts +13 -0
- package/src/types/messages.ts +61 -7
- package/src/types/transport.ts +11 -0
|
@@ -446,6 +446,9 @@
|
|
|
446
446
|
"MessageType_DeviceCertificateSignature": 60423,
|
|
447
447
|
"MessageType_DeviceCertificateSign": 60424,
|
|
448
448
|
"MessageType_DeviceMiscUsbMscControl": 60440,
|
|
449
|
+
"MessageType_DeviceFindMyTokenState": 60450,
|
|
450
|
+
"MessageType_DeviceFindMyTokenUpdate": 60451,
|
|
451
|
+
"MessageType_DeviceFindMyTokenStateGet": 60452,
|
|
449
452
|
"MessageType_DeviceInfoGet": 60600,
|
|
450
453
|
"MessageType_DeviceInfo": 60601,
|
|
451
454
|
"MessageType_DeviceStatusGet": 60602,
|
|
@@ -462,9 +465,10 @@
|
|
|
462
465
|
"MessageType_FilesystemDirMake": 60809,
|
|
463
466
|
"MessageType_FilesystemDirRemove": 60810,
|
|
464
467
|
"MessageType_FilesystemFormat": 60811,
|
|
465
|
-
"
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
+
"MessageType_DeviceFirmwareUpdateStage": 61000,
|
|
469
|
+
"MessageType_DeviceFirmwareUpdateRequest": 61001,
|
|
470
|
+
"MessageType_DeviceFirmwareUpdateStatusGet": 61002,
|
|
471
|
+
"MessageType_DeviceFirmwareUpdateStatus": 61003,
|
|
468
472
|
"MessageType_DeviceSessionGet": 61200,
|
|
469
473
|
"MessageType_DeviceSession": 61201,
|
|
470
474
|
"MessageType_DeviceSessionAskPin": 61202,
|
|
@@ -5403,6 +5407,10 @@
|
|
|
5403
5407
|
"request_id": {
|
|
5404
5408
|
"type": "bytes",
|
|
5405
5409
|
"id": 5
|
|
5410
|
+
},
|
|
5411
|
+
"source_fingerprint": {
|
|
5412
|
+
"type": "uint32",
|
|
5413
|
+
"id": 6
|
|
5406
5414
|
}
|
|
5407
5415
|
}
|
|
5408
5416
|
},
|
|
@@ -5538,6 +5546,14 @@
|
|
|
5538
5546
|
"tx_type": {
|
|
5539
5547
|
"type": "uint32",
|
|
5540
5548
|
"id": 10
|
|
5549
|
+
},
|
|
5550
|
+
"expected_address": {
|
|
5551
|
+
"type": "bytes",
|
|
5552
|
+
"id": 12
|
|
5553
|
+
},
|
|
5554
|
+
"source_fingerprint": {
|
|
5555
|
+
"type": "uint32",
|
|
5556
|
+
"id": 13
|
|
5541
5557
|
}
|
|
5542
5558
|
}
|
|
5543
5559
|
},
|
|
@@ -5618,6 +5634,14 @@
|
|
|
5618
5634
|
"rule": "repeated",
|
|
5619
5635
|
"type": "EthereumAccessListOneKey",
|
|
5620
5636
|
"id": 11
|
|
5637
|
+
},
|
|
5638
|
+
"expected_address": {
|
|
5639
|
+
"type": "bytes",
|
|
5640
|
+
"id": 12
|
|
5641
|
+
},
|
|
5642
|
+
"source_fingerprint": {
|
|
5643
|
+
"type": "uint32",
|
|
5644
|
+
"id": 13
|
|
5621
5645
|
}
|
|
5622
5646
|
}
|
|
5623
5647
|
},
|
|
@@ -5794,6 +5818,10 @@
|
|
|
5794
5818
|
"chain_id": {
|
|
5795
5819
|
"type": "uint64",
|
|
5796
5820
|
"id": 3
|
|
5821
|
+
},
|
|
5822
|
+
"source_fingerprint": {
|
|
5823
|
+
"type": "uint32",
|
|
5824
|
+
"id": 4
|
|
5797
5825
|
}
|
|
5798
5826
|
}
|
|
5799
5827
|
},
|
|
@@ -9473,6 +9501,10 @@
|
|
|
9473
9501
|
"rule": "required",
|
|
9474
9502
|
"type": "bytes",
|
|
9475
9503
|
"id": 2
|
|
9504
|
+
},
|
|
9505
|
+
"source_fingerprint": {
|
|
9506
|
+
"type": "uint32",
|
|
9507
|
+
"id": 3
|
|
9476
9508
|
}
|
|
9477
9509
|
}
|
|
9478
9510
|
},
|
|
@@ -9517,6 +9549,10 @@
|
|
|
9517
9549
|
"application_domain": {
|
|
9518
9550
|
"type": "bytes",
|
|
9519
9551
|
"id": 5
|
|
9552
|
+
},
|
|
9553
|
+
"source_fingerprint": {
|
|
9554
|
+
"type": "uint32",
|
|
9555
|
+
"id": 6
|
|
9520
9556
|
}
|
|
9521
9557
|
}
|
|
9522
9558
|
},
|
|
@@ -9534,6 +9570,10 @@
|
|
|
9534
9570
|
"rule": "required",
|
|
9535
9571
|
"type": "bytes",
|
|
9536
9572
|
"id": 2
|
|
9573
|
+
},
|
|
9574
|
+
"source_fingerprint": {
|
|
9575
|
+
"type": "uint32",
|
|
9576
|
+
"id": 3
|
|
9537
9577
|
}
|
|
9538
9578
|
}
|
|
9539
9579
|
},
|
|
@@ -11763,6 +11803,27 @@
|
|
|
11763
11803
|
}
|
|
11764
11804
|
}
|
|
11765
11805
|
},
|
|
11806
|
+
"DeviceFindMyTokenUpdate": {
|
|
11807
|
+
"fields": {
|
|
11808
|
+
"token": {
|
|
11809
|
+
"rule": "required",
|
|
11810
|
+
"type": "bytes",
|
|
11811
|
+
"id": 1
|
|
11812
|
+
}
|
|
11813
|
+
}
|
|
11814
|
+
},
|
|
11815
|
+
"DeviceFindMyTokenStateGet": {
|
|
11816
|
+
"fields": {}
|
|
11817
|
+
},
|
|
11818
|
+
"DeviceFindMyTokenState": {
|
|
11819
|
+
"fields": {
|
|
11820
|
+
"burned": {
|
|
11821
|
+
"rule": "required",
|
|
11822
|
+
"type": "bool",
|
|
11823
|
+
"id": 1
|
|
11824
|
+
}
|
|
11825
|
+
}
|
|
11826
|
+
},
|
|
11766
11827
|
"DeviceFactoryAck": {
|
|
11767
11828
|
"values": {
|
|
11768
11829
|
"FACTORY_ACK_SUCCESS": 0,
|
|
@@ -11813,14 +11874,14 @@
|
|
|
11813
11874
|
"type": "string",
|
|
11814
11875
|
"id": 2
|
|
11815
11876
|
},
|
|
11816
|
-
"burn_in_completed": {
|
|
11817
|
-
"type": "bool",
|
|
11818
|
-
"id": 3
|
|
11819
|
-
},
|
|
11820
11877
|
"factory_test_completed": {
|
|
11821
11878
|
"type": "bool",
|
|
11822
11879
|
"id": 4
|
|
11823
11880
|
},
|
|
11881
|
+
"factory_burn_in_completed": {
|
|
11882
|
+
"type": "bool",
|
|
11883
|
+
"id": 3
|
|
11884
|
+
},
|
|
11824
11885
|
"manufacture_time": {
|
|
11825
11886
|
"type": "DeviceFactoryInfoManufactureTime",
|
|
11826
11887
|
"id": 5
|
|
@@ -11892,6 +11953,13 @@
|
|
|
11892
11953
|
"FW_MGMT_UPDATER_TASK_STATUS_FAILED_ENTRY_OUT_OF_BOUNDS": 10
|
|
11893
11954
|
}
|
|
11894
11955
|
},
|
|
11956
|
+
"DeviceFirmwareUpdatePhase": {
|
|
11957
|
+
"values": {
|
|
11958
|
+
"FW_MGMT_UPDATER_PHASE_PREPARE": 0,
|
|
11959
|
+
"FW_MGMT_UPDATER_PHASE_INSTALL": 1,
|
|
11960
|
+
"FW_MGMT_UPDATER_PHASE_VERIFY": 2
|
|
11961
|
+
}
|
|
11962
|
+
},
|
|
11895
11963
|
"DeviceFirmwareTarget": {
|
|
11896
11964
|
"fields": {
|
|
11897
11965
|
"target_id": {
|
|
@@ -11906,7 +11974,7 @@
|
|
|
11906
11974
|
}
|
|
11907
11975
|
}
|
|
11908
11976
|
},
|
|
11909
|
-
"
|
|
11977
|
+
"DeviceFirmwareUpdateStage": {
|
|
11910
11978
|
"fields": {
|
|
11911
11979
|
"targets": {
|
|
11912
11980
|
"rule": "repeated",
|
|
@@ -11915,6 +11983,28 @@
|
|
|
11915
11983
|
}
|
|
11916
11984
|
}
|
|
11917
11985
|
},
|
|
11986
|
+
"DeviceFirmwareUpdateRequest": {
|
|
11987
|
+
"fields": {
|
|
11988
|
+
"reboot_after_update": {
|
|
11989
|
+
"type": "bool",
|
|
11990
|
+
"id": 1
|
|
11991
|
+
}
|
|
11992
|
+
}
|
|
11993
|
+
},
|
|
11994
|
+
"DeviceFirmwareUpdatePhaseInfo": {
|
|
11995
|
+
"fields": {
|
|
11996
|
+
"phase": {
|
|
11997
|
+
"rule": "required",
|
|
11998
|
+
"type": "DeviceFirmwareUpdatePhase",
|
|
11999
|
+
"id": 1
|
|
12000
|
+
},
|
|
12001
|
+
"progress_percent": {
|
|
12002
|
+
"rule": "required",
|
|
12003
|
+
"type": "uint32",
|
|
12004
|
+
"id": 2
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
},
|
|
11918
12008
|
"DeviceFirmwareUpdateRecord": {
|
|
11919
12009
|
"fields": {
|
|
11920
12010
|
"target_id": {
|
|
@@ -11926,6 +12016,14 @@
|
|
|
11926
12016
|
"type": "DeviceFirmwareUpdateTaskStatus",
|
|
11927
12017
|
"id": 10
|
|
11928
12018
|
},
|
|
12019
|
+
"progress_percent": {
|
|
12020
|
+
"type": "uint32",
|
|
12021
|
+
"id": 11
|
|
12022
|
+
},
|
|
12023
|
+
"phase_info": {
|
|
12024
|
+
"type": "DeviceFirmwareUpdatePhaseInfo",
|
|
12025
|
+
"id": 12
|
|
12026
|
+
},
|
|
11929
12027
|
"payload_version": {
|
|
11930
12028
|
"type": "uint32",
|
|
11931
12029
|
"id": 20
|
|
@@ -11942,6 +12040,14 @@
|
|
|
11942
12040
|
"type": "bool",
|
|
11943
12041
|
"id": 10
|
|
11944
12042
|
},
|
|
12043
|
+
"progress_percent": {
|
|
12044
|
+
"type": "bool",
|
|
12045
|
+
"id": 11
|
|
12046
|
+
},
|
|
12047
|
+
"phase_info": {
|
|
12048
|
+
"type": "bool",
|
|
12049
|
+
"id": 12
|
|
12050
|
+
},
|
|
11945
12051
|
"payload_version": {
|
|
11946
12052
|
"type": "bool",
|
|
11947
12053
|
"id": 20
|
|
@@ -12096,7 +12202,7 @@
|
|
|
12096
12202
|
"type": "bool",
|
|
12097
12203
|
"id": 100
|
|
12098
12204
|
},
|
|
12099
|
-
"
|
|
12205
|
+
"main_mcu": {
|
|
12100
12206
|
"type": "bool",
|
|
12101
12207
|
"id": 200
|
|
12102
12208
|
},
|
|
@@ -12165,7 +12271,7 @@
|
|
|
12165
12271
|
"type": "DeviceHardwareInfo",
|
|
12166
12272
|
"id": 100
|
|
12167
12273
|
},
|
|
12168
|
-
"
|
|
12274
|
+
"main_mcu": {
|
|
12169
12275
|
"type": "DeviceMainMcuInfo",
|
|
12170
12276
|
"id": 200
|
|
12171
12277
|
},
|
|
@@ -12703,9 +12809,16 @@
|
|
|
12703
12809
|
"id": 1
|
|
12704
12810
|
},
|
|
12705
12811
|
"text": {
|
|
12706
|
-
"rule": "required",
|
|
12707
12812
|
"type": "string",
|
|
12708
12813
|
"id": 2
|
|
12814
|
+
},
|
|
12815
|
+
"text_id": {
|
|
12816
|
+
"type": "uint32",
|
|
12817
|
+
"id": 3
|
|
12818
|
+
},
|
|
12819
|
+
"text_arg": {
|
|
12820
|
+
"type": "string",
|
|
12821
|
+
"id": 4
|
|
12709
12822
|
}
|
|
12710
12823
|
}
|
|
12711
12824
|
},
|
|
@@ -12736,7 +12849,6 @@
|
|
|
12736
12849
|
"ViewSignPage": {
|
|
12737
12850
|
"fields": {
|
|
12738
12851
|
"title": {
|
|
12739
|
-
"rule": "required",
|
|
12740
12852
|
"type": "string",
|
|
12741
12853
|
"id": 1
|
|
12742
12854
|
},
|
|
@@ -12770,13 +12882,20 @@
|
|
|
12770
12882
|
"options": {
|
|
12771
12883
|
"default": "LayoutDefault"
|
|
12772
12884
|
}
|
|
12885
|
+
},
|
|
12886
|
+
"title_id": {
|
|
12887
|
+
"type": "uint32",
|
|
12888
|
+
"id": 8
|
|
12889
|
+
},
|
|
12890
|
+
"title_arg": {
|
|
12891
|
+
"type": "string",
|
|
12892
|
+
"id": 9
|
|
12773
12893
|
}
|
|
12774
12894
|
}
|
|
12775
12895
|
},
|
|
12776
12896
|
"ViewVerifyPage": {
|
|
12777
12897
|
"fields": {
|
|
12778
12898
|
"title": {
|
|
12779
|
-
"rule": "required",
|
|
12780
12899
|
"type": "string",
|
|
12781
12900
|
"id": 1
|
|
12782
12901
|
},
|
|
@@ -12801,6 +12920,14 @@
|
|
|
12801
12920
|
"value_key": {
|
|
12802
12921
|
"type": "uint32",
|
|
12803
12922
|
"id": 6
|
|
12923
|
+
},
|
|
12924
|
+
"title_id": {
|
|
12925
|
+
"type": "uint32",
|
|
12926
|
+
"id": 7
|
|
12927
|
+
},
|
|
12928
|
+
"chain_id": {
|
|
12929
|
+
"type": "uint32",
|
|
12930
|
+
"id": 8
|
|
12804
12931
|
}
|
|
12805
12932
|
}
|
|
12806
12933
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport",
|
|
3
|
-
"version": "1.2.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Transport layer abstractions and utilities for OneKey hardware SDK.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"long": "^4.0.0",
|
|
29
29
|
"protobufjs": "^6.11.2"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "cc51df0a415bead21304ad1d8bc92b5312c91344"
|
|
32
32
|
}
|
package/src/protocols/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ByteBuffer from 'bytebuffer';
|
|
2
|
+
import { Reader, Type } from 'protobufjs/light';
|
|
2
3
|
|
|
3
4
|
import { encodeEnvelopeMessage, encodeMessageChunks, encodeTransportPackets } from './v1/packets';
|
|
4
5
|
import { decodeFirstChunk } from './v1/decode';
|
|
@@ -29,6 +30,85 @@ type ProtocolV2FrameOptions = {
|
|
|
29
30
|
seq?: number;
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
const LEGACY_PROTOCOL_V2_PROTOCOL_INFO = Type.fromJSON('LegacyProtocolInfo', {
|
|
34
|
+
fields: {
|
|
35
|
+
version: {
|
|
36
|
+
rule: 'required',
|
|
37
|
+
type: 'uint32',
|
|
38
|
+
id: 1,
|
|
39
|
+
},
|
|
40
|
+
supported_messages: {
|
|
41
|
+
rule: 'repeated',
|
|
42
|
+
type: 'uint32',
|
|
43
|
+
id: 2,
|
|
44
|
+
options: {
|
|
45
|
+
packed: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
protobuf_definition: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
id: 3,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const decodeLegacyProtocolV2ProtocolInfo = (pbPayload: Uint8Array) => {
|
|
56
|
+
const byteBuffer = ByteBuffer.wrap(Buffer.from(pbPayload) as unknown as ArrayBuffer);
|
|
57
|
+
const legacy = decodeProtobuf(LEGACY_PROTOCOL_V2_PROTOCOL_INFO, byteBuffer);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
version: legacy.version,
|
|
61
|
+
build_fingerprint: '',
|
|
62
|
+
supported_messages: legacy.supported_messages ?? [],
|
|
63
|
+
// Presence of this legacy field lets Core keep the fallback recovery-only.
|
|
64
|
+
protobuf_definition: null,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const isLegacyProtocolV2ProtocolInfoWireLayout = (pbPayload: Uint8Array) => {
|
|
69
|
+
const reader = Reader.create(pbPayload);
|
|
70
|
+
let hasVersion = false;
|
|
71
|
+
try {
|
|
72
|
+
while (reader.pos < reader.len) {
|
|
73
|
+
const tag = reader.uint32();
|
|
74
|
+
// eslint-disable-next-line no-bitwise
|
|
75
|
+
const fieldNumber = tag >>> 3;
|
|
76
|
+
// eslint-disable-next-line no-bitwise
|
|
77
|
+
const wireType = tag & 0x07;
|
|
78
|
+
if (fieldNumber === 1) {
|
|
79
|
+
if (wireType !== 0) return false;
|
|
80
|
+
hasVersion = true;
|
|
81
|
+
} else if (fieldNumber === 2) {
|
|
82
|
+
// Legacy field 2 is repeated uint32; current field 2 is a string.
|
|
83
|
+
if (wireType !== 0) return false;
|
|
84
|
+
} else if (fieldNumber === 3) {
|
|
85
|
+
// Legacy field 3 is protobuf_definition; current field 3 is uint32.
|
|
86
|
+
if (wireType !== 2) return false;
|
|
87
|
+
} else {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
reader.skipType(wireType);
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return hasVersion;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const tryDecodeLegacyProtocolV2ProtocolInfo = (messageName: string, pbPayload: Uint8Array) => {
|
|
99
|
+
// TEMPORARY COMPATIBILITY: Remove this fallback after all supported Pro2 and Neo
|
|
100
|
+
// firmware versions return ProtocolInfo with the current build_fingerprint layout.
|
|
101
|
+
if (messageName !== 'ProtocolInfo' || !isLegacyProtocolV2ProtocolInfoWireLayout(pbPayload)) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
return decodeLegacyProtocolV2ProtocolInfo(pbPayload);
|
|
107
|
+
} catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
32
112
|
const resolveProtocolV2EncodeSchema = (name: string, schemas: ProtocolV2Schemas) => {
|
|
33
113
|
try {
|
|
34
114
|
schemas.protocolV2.lookupType(name);
|
|
@@ -123,6 +203,17 @@ export const ProtocolV2 = {
|
|
|
123
203
|
try {
|
|
124
204
|
message = decodeProtobuf(Message, rxByteBuffer);
|
|
125
205
|
} catch (cause) {
|
|
206
|
+
const legacyProtocolInfo = tryDecodeLegacyProtocolV2ProtocolInfo(messageName, pbPayload);
|
|
207
|
+
if (legacyProtocolInfo) {
|
|
208
|
+
return {
|
|
209
|
+
message: legacyProtocolInfo,
|
|
210
|
+
messageName,
|
|
211
|
+
messageTypeId,
|
|
212
|
+
pbPayload,
|
|
213
|
+
seq,
|
|
214
|
+
type: messageName,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
126
217
|
const error = new Error(
|
|
127
218
|
`Protocol V2 protobuf decode failed for "${messageName}" ` +
|
|
128
219
|
`(${messageTypeId}, ${pbPayload.length}-byte payload); ` +
|
|
@@ -23,3 +23,32 @@ export class ProtocolV2LinkError extends Error {
|
|
|
23
23
|
|
|
24
24
|
export const isProtocolV2LinkError = (error: unknown): error is ProtocolV2LinkError =>
|
|
25
25
|
error instanceof ProtocolV2LinkError;
|
|
26
|
+
|
|
27
|
+
export type ProtocolV2LinkDisabledError = Error & {
|
|
28
|
+
name: 'ProtocolV2LinkDisabledError';
|
|
29
|
+
failureCode: string | number;
|
|
30
|
+
firmwareMessage: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const createProtocolV2LinkDisabledError = (
|
|
34
|
+
failureCode: string | number,
|
|
35
|
+
firmwareMessage: string
|
|
36
|
+
): ProtocolV2LinkDisabledError =>
|
|
37
|
+
Object.assign(new Error(firmwareMessage), {
|
|
38
|
+
name: 'ProtocolV2LinkDisabledError' as const,
|
|
39
|
+
failureCode,
|
|
40
|
+
firmwareMessage,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const isProtocolV2LinkDisabledError = (
|
|
44
|
+
error: unknown
|
|
45
|
+
): error is ProtocolV2LinkDisabledError =>
|
|
46
|
+
error instanceof Error &&
|
|
47
|
+
error.name === 'ProtocolV2LinkDisabledError' &&
|
|
48
|
+
'failureCode' in error &&
|
|
49
|
+
'firmwareMessage' in error;
|
|
50
|
+
|
|
51
|
+
export const isProtocolV2LinkDisabledFailure = (failureCode: unknown, firmwareMessage: unknown) =>
|
|
52
|
+
(failureCode === 'Failure_ProcessError' || failureCode === 5) &&
|
|
53
|
+
typeof firmwareMessage === 'string' &&
|
|
54
|
+
firmwareMessage.trim().toLowerCase() === 'link disabled';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProtocolV2SequenceCursor } from './sequence-cursor';
|
|
2
2
|
import { ProtocolV2LinkError } from './errors';
|
|
3
3
|
import { ProtocolV2Session, getErrorMessage } from './session';
|
|
4
|
+
import { PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS } from '../../constants';
|
|
4
5
|
|
|
5
6
|
import type { MessageFromOneKey, TransportCallOptions } from '../../types';
|
|
6
7
|
import type { ProtocolV2CallContext, ProtocolV2Schemas, ProtocolV2SessionOptions } from './session';
|
|
@@ -61,15 +62,45 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
61
62
|
options?: TransportCallOptions
|
|
62
63
|
): Promise<MessageFromOneKey> {
|
|
63
64
|
const generation = this.generations.get(key) ?? 0;
|
|
65
|
+
const previousQueue = this.callQueues.get(key);
|
|
66
|
+
const timeoutMs =
|
|
67
|
+
options?.timeoutMs && options.timeoutMs > 0
|
|
68
|
+
? options.timeoutMs
|
|
69
|
+
: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
|
|
70
|
+
const queuedAt = Date.now();
|
|
71
|
+
let queueTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
72
|
+
let queueTimeoutError: Error | undefined;
|
|
73
|
+
const timeoutWhileQueued = new Promise<never>((_, reject) => {
|
|
74
|
+
queueTimeout = setTimeout(() => {
|
|
75
|
+
queueTimeout = undefined;
|
|
76
|
+
queueTimeoutError = this.createQueuedCallTimeoutError(key, name, timeoutMs);
|
|
77
|
+
reject(queueTimeoutError);
|
|
78
|
+
}, timeoutMs);
|
|
79
|
+
});
|
|
64
80
|
const run = () => {
|
|
81
|
+
if (queueTimeout) {
|
|
82
|
+
clearTimeout(queueTimeout);
|
|
83
|
+
queueTimeout = undefined;
|
|
84
|
+
}
|
|
85
|
+
if (queueTimeoutError) {
|
|
86
|
+
throw queueTimeoutError;
|
|
87
|
+
}
|
|
65
88
|
this.assertCallGeneration(key, generation);
|
|
66
|
-
|
|
89
|
+
const remainingTimeoutMs = previousQueue ? timeoutMs - (Date.now() - queuedAt) : timeoutMs;
|
|
90
|
+
if (remainingTimeoutMs <= 0) {
|
|
91
|
+
throw this.createQueuedCallTimeoutError(key, name, timeoutMs);
|
|
92
|
+
}
|
|
93
|
+
return this.executeCall(key, createAdapter, name, data, {
|
|
94
|
+
...options,
|
|
95
|
+
timeoutMs: remainingTimeoutMs,
|
|
96
|
+
});
|
|
67
97
|
};
|
|
68
|
-
const previous =
|
|
69
|
-
const
|
|
70
|
-
const
|
|
98
|
+
const previous = previousQueue ?? Promise.resolve();
|
|
99
|
+
const execution = previous.then(run, run);
|
|
100
|
+
const result = Promise.race([execution, timeoutWhileQueued]);
|
|
101
|
+
const queue = execution.catch(() => undefined);
|
|
71
102
|
this.callQueues.set(key, queue);
|
|
72
|
-
|
|
103
|
+
execution
|
|
73
104
|
.then(
|
|
74
105
|
() => this.clearSettledCallQueue(key, queue),
|
|
75
106
|
() => this.clearSettledCallQueue(key, queue)
|
|
@@ -78,6 +109,17 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
78
109
|
return result;
|
|
79
110
|
}
|
|
80
111
|
|
|
112
|
+
sendFlowControl(
|
|
113
|
+
key: Key,
|
|
114
|
+
createAdapter: () => ProtocolV2LinkAdapter,
|
|
115
|
+
name: string,
|
|
116
|
+
data: Record<string, unknown>
|
|
117
|
+
): Promise<MessageFromOneKey> {
|
|
118
|
+
const generation = this.generations.get(key) ?? 0;
|
|
119
|
+
this.assertCallGeneration(key, generation);
|
|
120
|
+
return this.getOrCreateLink(key, createAdapter).session.sendFlowControl(name, data);
|
|
121
|
+
}
|
|
122
|
+
|
|
81
123
|
async invalidateLink(key: Key, reason: string): Promise<void> {
|
|
82
124
|
const generation = (this.generations.get(key) ?? 0) + 1;
|
|
83
125
|
this.generations.set(key, generation);
|
|
@@ -195,6 +237,16 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
195
237
|
}
|
|
196
238
|
}
|
|
197
239
|
|
|
240
|
+
private createQueuedCallTimeoutError(key: Key, name: string, timeoutMs: number): Error {
|
|
241
|
+
const adapterTimeoutError = this.links.get(key)?.adapter.createTimeoutError;
|
|
242
|
+
return adapterTimeoutError
|
|
243
|
+
? adapterTimeoutError(name, timeoutMs)
|
|
244
|
+
: new ProtocolV2LinkError(
|
|
245
|
+
'response-timeout',
|
|
246
|
+
`Protocol V2 call timeout after ${timeoutMs}ms while queued for ${name}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
198
250
|
private assertCallGeneration(key: Key, generation: number) {
|
|
199
251
|
const currentGeneration = this.generations.get(key) ?? 0;
|
|
200
252
|
if (currentGeneration === generation) return;
|