@onekeyfe/hd-transport 1.2.0-alpha.4 → 1.2.0-alpha.40
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/__tests__/messages.test.js +187 -0
- package/__tests__/protocol-v2-ble-frame-writer.test.js +119 -0
- package/__tests__/protocol-v2-link-manager.test.js +437 -0
- package/__tests__/protocol-v2-usb-transport-base.test.js +330 -0
- package/__tests__/protocol-v2.test.js +465 -56
- package/__tests__/transport-log.test.js +79 -0
- package/dist/constants.d.ts +4 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +790 -198
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +820 -227
- package/dist/protocols/index.d.ts +14 -5
- package/dist/protocols/index.d.ts.map +1 -1
- package/dist/protocols/v2/ble-frame-writer.d.ts +15 -0
- package/dist/protocols/v2/ble-frame-writer.d.ts.map +1 -0
- package/dist/protocols/v2/constants.d.ts +1 -0
- package/dist/protocols/v2/constants.d.ts.map +1 -1
- package/dist/protocols/v2/decode.d.ts +13 -2
- package/dist/protocols/v2/decode.d.ts.map +1 -1
- package/dist/protocols/v2/encode.d.ts +2 -3
- package/dist/protocols/v2/encode.d.ts.map +1 -1
- package/dist/protocols/v2/errors.d.ts +8 -0
- package/dist/protocols/v2/errors.d.ts.map +1 -0
- package/dist/protocols/v2/frame-assembler.d.ts.map +1 -1
- package/dist/protocols/v2/index.d.ts +2 -1
- package/dist/protocols/v2/index.d.ts.map +1 -1
- package/dist/protocols/v2/link-manager.d.ts +39 -0
- package/dist/protocols/v2/link-manager.d.ts.map +1 -0
- package/dist/protocols/v2/sequence-cursor.d.ts +5 -0
- package/dist/protocols/v2/sequence-cursor.d.ts.map +1 -0
- package/dist/protocols/v2/session.d.ts +19 -5
- package/dist/protocols/v2/session.d.ts.map +1 -1
- package/dist/protocols/v2/usb-transport-base.d.ts +32 -0
- package/dist/protocols/v2/usb-transport-base.d.ts.map +1 -0
- package/dist/serialization/protobuf/decode.d.ts.map +1 -1
- package/dist/types/messages.d.ts +380 -110
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/transport.d.ts +14 -3
- package/dist/types/transport.d.ts.map +1 -1
- package/dist/utils/transportLog.d.ts +9 -0
- package/dist/utils/transportLog.d.ts.map +1 -0
- package/messages-protocol-v2.json +1161 -477
- package/package.json +3 -3
- package/scripts/protobuf-build.sh +86 -208
- package/scripts/protobuf-patches/index.js +1 -0
- package/scripts/protobuf-types.js +19 -1
- package/src/constants.ts +28 -14
- package/src/index.ts +12 -1
- package/src/protocols/index.ts +39 -40
- package/src/protocols/v2/ble-frame-writer.ts +77 -0
- package/src/protocols/v2/constants.ts +1 -0
- package/src/protocols/v2/crc8.ts +1 -1
- package/src/protocols/v2/decode.ts +71 -37
- package/src/protocols/v2/encode.ts +2 -28
- package/src/protocols/v2/errors.ts +25 -0
- package/src/protocols/v2/frame-assembler.ts +6 -4
- package/src/protocols/v2/index.ts +2 -1
- package/src/protocols/v2/link-manager.ts +209 -0
- package/src/protocols/v2/sequence-cursor.ts +10 -0
- package/src/protocols/v2/session.ts +157 -200
- package/src/protocols/v2/usb-transport-base.ts +213 -0
- package/src/serialization/protobuf/decode.ts +4 -1
- package/src/types/messages.ts +464 -142
- package/src/types/transport.ts +14 -3
- package/src/utils/transportLog.ts +102 -0
- package/dist/protocols/v2/debug.d.ts +0 -13
- package/dist/protocols/v2/debug.d.ts.map +0 -1
- package/src/protocols/v2/debug.ts +0 -26
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { ProtocolV2SequenceCursor } from './sequence-cursor';
|
|
2
|
+
import { ProtocolV2LinkError } from './errors';
|
|
3
|
+
import { ProtocolV2Session, getErrorMessage } from './session';
|
|
4
|
+
|
|
5
|
+
import type { MessageFromOneKey, TransportCallOptions } from '../../types';
|
|
6
|
+
import type { ProtocolV2CallContext, ProtocolV2Schemas, ProtocolV2SessionOptions } from './session';
|
|
7
|
+
|
|
8
|
+
export type ProtocolV2LinkErrorClassification = 'link-fatal' | 'recoverable';
|
|
9
|
+
|
|
10
|
+
export interface ProtocolV2LinkAdapter {
|
|
11
|
+
router: number;
|
|
12
|
+
maxFrameBytes?: number;
|
|
13
|
+
generation: number;
|
|
14
|
+
prepareCall(context: ProtocolV2CallContext): Promise<void> | void;
|
|
15
|
+
writeFrame(frame: Uint8Array, context: ProtocolV2CallContext): Promise<void>;
|
|
16
|
+
readFrame(context: ProtocolV2CallContext): Promise<Uint8Array>;
|
|
17
|
+
reset(reason: string): Promise<void> | void;
|
|
18
|
+
logger?: ProtocolV2SessionOptions['logger'];
|
|
19
|
+
logPrefix?: string;
|
|
20
|
+
createTimeoutError?: ProtocolV2SessionOptions['createTimeoutError'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ProtocolV2LinkManagerOptions<Key> = {
|
|
24
|
+
getSchemas: () => ProtocolV2Schemas;
|
|
25
|
+
classifyError: (error: unknown) => ProtocolV2LinkErrorClassification;
|
|
26
|
+
onLinkInvalidated?: (key: Key, reason: string) => Promise<void> | void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type ProtocolV2Link = {
|
|
30
|
+
adapter: ProtocolV2LinkAdapter;
|
|
31
|
+
session: ProtocolV2Session;
|
|
32
|
+
state: {
|
|
33
|
+
invalidatedReason?: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export class ProtocolV2LinkManager<Key> {
|
|
38
|
+
private readonly links = new Map<Key, ProtocolV2Link>();
|
|
39
|
+
|
|
40
|
+
private readonly sequences = new Map<Key, ProtocolV2SequenceCursor>();
|
|
41
|
+
|
|
42
|
+
private readonly callQueues = new Map<Key, Promise<unknown>>();
|
|
43
|
+
|
|
44
|
+
private readonly generations = new Map<Key, number>();
|
|
45
|
+
|
|
46
|
+
private readonly invalidationReasons = new Map<Key, { generation: number; reason: string }>();
|
|
47
|
+
|
|
48
|
+
private readonly invalidations = new Map<Key, Promise<void>>();
|
|
49
|
+
|
|
50
|
+
private readonly options: ProtocolV2LinkManagerOptions<Key>;
|
|
51
|
+
|
|
52
|
+
constructor(options: ProtocolV2LinkManagerOptions<Key>) {
|
|
53
|
+
this.options = options;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
call(
|
|
57
|
+
key: Key,
|
|
58
|
+
createAdapter: () => ProtocolV2LinkAdapter,
|
|
59
|
+
name: string,
|
|
60
|
+
data: Record<string, unknown>,
|
|
61
|
+
options?: TransportCallOptions
|
|
62
|
+
): Promise<MessageFromOneKey> {
|
|
63
|
+
const generation = this.generations.get(key) ?? 0;
|
|
64
|
+
const run = () => {
|
|
65
|
+
this.assertCallGeneration(key, generation);
|
|
66
|
+
return this.executeCall(key, createAdapter, name, data, options);
|
|
67
|
+
};
|
|
68
|
+
const previous = this.callQueues.get(key) ?? Promise.resolve();
|
|
69
|
+
const result = previous.then(run, run);
|
|
70
|
+
const queue = result.catch(() => undefined);
|
|
71
|
+
this.callQueues.set(key, queue);
|
|
72
|
+
result
|
|
73
|
+
.then(
|
|
74
|
+
() => this.clearSettledCallQueue(key, queue),
|
|
75
|
+
() => this.clearSettledCallQueue(key, queue)
|
|
76
|
+
)
|
|
77
|
+
.catch(() => undefined);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async invalidateLink(key: Key, reason: string): Promise<void> {
|
|
82
|
+
const generation = (this.generations.get(key) ?? 0) + 1;
|
|
83
|
+
this.generations.set(key, generation);
|
|
84
|
+
this.invalidationReasons.set(key, { generation, reason });
|
|
85
|
+
|
|
86
|
+
const pendingInvalidation = this.invalidations.get(key);
|
|
87
|
+
const link = this.links.get(key);
|
|
88
|
+
if (!link) {
|
|
89
|
+
await pendingInvalidation;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.links.delete(key);
|
|
94
|
+
link.state.invalidatedReason = reason;
|
|
95
|
+
const invalidation = (async () => {
|
|
96
|
+
await pendingInvalidation?.catch(() => undefined);
|
|
97
|
+
await link.adapter.reset(reason);
|
|
98
|
+
await this.options.onLinkInvalidated?.(key, reason);
|
|
99
|
+
})();
|
|
100
|
+
this.invalidations.set(key, invalidation);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await invalidation;
|
|
104
|
+
} finally {
|
|
105
|
+
if (this.invalidations.get(key) === invalidation) {
|
|
106
|
+
this.invalidations.delete(key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async invalidateAllLinks(reason: string): Promise<void> {
|
|
112
|
+
const keys = new Set([
|
|
113
|
+
...this.links.keys(),
|
|
114
|
+
...this.callQueues.keys(),
|
|
115
|
+
...this.invalidations.keys(),
|
|
116
|
+
]);
|
|
117
|
+
await Promise.all(Array.from(keys, key => this.invalidateLink(key, reason)));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async dispose(reason: string): Promise<void> {
|
|
121
|
+
await this.invalidateAllLinks(reason);
|
|
122
|
+
this.sequences.clear();
|
|
123
|
+
this.callQueues.clear();
|
|
124
|
+
this.invalidations.clear();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private getOrCreateLink(key: Key, createAdapter: () => ProtocolV2LinkAdapter): ProtocolV2Link {
|
|
128
|
+
const existing = this.links.get(key);
|
|
129
|
+
if (existing) return existing;
|
|
130
|
+
|
|
131
|
+
const adapter = createAdapter();
|
|
132
|
+
const state: ProtocolV2Link['state'] = {};
|
|
133
|
+
const assertLinkActive = () => {
|
|
134
|
+
if (state.invalidatedReason) {
|
|
135
|
+
throw new Error(state.invalidatedReason);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
let sequenceCursor = this.sequences.get(key);
|
|
139
|
+
if (!sequenceCursor) {
|
|
140
|
+
sequenceCursor = new ProtocolV2SequenceCursor();
|
|
141
|
+
this.sequences.set(key, sequenceCursor);
|
|
142
|
+
}
|
|
143
|
+
const session = new ProtocolV2Session({
|
|
144
|
+
schemas: this.options.getSchemas(),
|
|
145
|
+
router: adapter.router,
|
|
146
|
+
maxFrameBytes: adapter.maxFrameBytes,
|
|
147
|
+
generation: adapter.generation,
|
|
148
|
+
sequenceCursor,
|
|
149
|
+
prepareCall: async context => {
|
|
150
|
+
assertLinkActive();
|
|
151
|
+
await adapter.prepareCall(context);
|
|
152
|
+
assertLinkActive();
|
|
153
|
+
},
|
|
154
|
+
writeFrame: async (frame, context) => {
|
|
155
|
+
assertLinkActive();
|
|
156
|
+
await adapter.writeFrame(frame, context);
|
|
157
|
+
assertLinkActive();
|
|
158
|
+
},
|
|
159
|
+
readFrame: async context => {
|
|
160
|
+
assertLinkActive();
|
|
161
|
+
const frame = await adapter.readFrame(context);
|
|
162
|
+
assertLinkActive();
|
|
163
|
+
return frame;
|
|
164
|
+
},
|
|
165
|
+
logger: adapter.logger,
|
|
166
|
+
logPrefix: adapter.logPrefix,
|
|
167
|
+
createTimeoutError: adapter.createTimeoutError,
|
|
168
|
+
});
|
|
169
|
+
const link = { adapter, session, state };
|
|
170
|
+
this.links.set(key, link);
|
|
171
|
+
return link;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private async executeCall(
|
|
175
|
+
key: Key,
|
|
176
|
+
createAdapter: () => ProtocolV2LinkAdapter,
|
|
177
|
+
name: string,
|
|
178
|
+
data: Record<string, unknown>,
|
|
179
|
+
options?: TransportCallOptions
|
|
180
|
+
): Promise<MessageFromOneKey> {
|
|
181
|
+
try {
|
|
182
|
+
return await this.getOrCreateLink(key, createAdapter).session.call(name, data, options);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (this.options.classifyError(error) === 'link-fatal') {
|
|
185
|
+
const errorMessage = getErrorMessage(error) || 'unknown error';
|
|
186
|
+
await this.invalidateLink(key, `Protocol V2 link-fatal error: ${errorMessage}`);
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private clearSettledCallQueue(key: Key, queue: Promise<unknown>) {
|
|
193
|
+
if (this.callQueues.get(key) === queue) {
|
|
194
|
+
this.callQueues.delete(key);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private assertCallGeneration(key: Key, generation: number) {
|
|
199
|
+
const currentGeneration = this.generations.get(key) ?? 0;
|
|
200
|
+
if (currentGeneration === generation) return;
|
|
201
|
+
|
|
202
|
+
const invalidation = this.invalidationReasons.get(key);
|
|
203
|
+
const reason =
|
|
204
|
+
invalidation?.generation === currentGeneration
|
|
205
|
+
? invalidation.reason
|
|
206
|
+
: 'Protocol V2 link generation changed';
|
|
207
|
+
throw new ProtocolV2LinkError('generation', reason);
|
|
208
|
+
}
|
|
209
|
+
}
|