@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.
Files changed (68) hide show
  1. package/__tests__/messages.test.js +187 -0
  2. package/__tests__/protocol-v2-ble-frame-writer.test.js +119 -0
  3. package/__tests__/protocol-v2-link-manager.test.js +437 -0
  4. package/__tests__/protocol-v2-usb-transport-base.test.js +330 -0
  5. package/__tests__/protocol-v2.test.js +465 -56
  6. package/__tests__/transport-log.test.js +79 -0
  7. package/dist/constants.d.ts +4 -1
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/index.d.ts +790 -198
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +820 -227
  12. package/dist/protocols/index.d.ts +14 -5
  13. package/dist/protocols/index.d.ts.map +1 -1
  14. package/dist/protocols/v2/ble-frame-writer.d.ts +15 -0
  15. package/dist/protocols/v2/ble-frame-writer.d.ts.map +1 -0
  16. package/dist/protocols/v2/constants.d.ts +1 -0
  17. package/dist/protocols/v2/constants.d.ts.map +1 -1
  18. package/dist/protocols/v2/decode.d.ts +13 -2
  19. package/dist/protocols/v2/decode.d.ts.map +1 -1
  20. package/dist/protocols/v2/encode.d.ts +2 -3
  21. package/dist/protocols/v2/encode.d.ts.map +1 -1
  22. package/dist/protocols/v2/errors.d.ts +8 -0
  23. package/dist/protocols/v2/errors.d.ts.map +1 -0
  24. package/dist/protocols/v2/frame-assembler.d.ts.map +1 -1
  25. package/dist/protocols/v2/index.d.ts +2 -1
  26. package/dist/protocols/v2/index.d.ts.map +1 -1
  27. package/dist/protocols/v2/link-manager.d.ts +39 -0
  28. package/dist/protocols/v2/link-manager.d.ts.map +1 -0
  29. package/dist/protocols/v2/sequence-cursor.d.ts +5 -0
  30. package/dist/protocols/v2/sequence-cursor.d.ts.map +1 -0
  31. package/dist/protocols/v2/session.d.ts +19 -5
  32. package/dist/protocols/v2/session.d.ts.map +1 -1
  33. package/dist/protocols/v2/usb-transport-base.d.ts +32 -0
  34. package/dist/protocols/v2/usb-transport-base.d.ts.map +1 -0
  35. package/dist/serialization/protobuf/decode.d.ts.map +1 -1
  36. package/dist/types/messages.d.ts +380 -110
  37. package/dist/types/messages.d.ts.map +1 -1
  38. package/dist/types/transport.d.ts +14 -3
  39. package/dist/types/transport.d.ts.map +1 -1
  40. package/dist/utils/transportLog.d.ts +9 -0
  41. package/dist/utils/transportLog.d.ts.map +1 -0
  42. package/messages-protocol-v2.json +1161 -477
  43. package/package.json +3 -3
  44. package/scripts/protobuf-build.sh +86 -208
  45. package/scripts/protobuf-patches/index.js +1 -0
  46. package/scripts/protobuf-types.js +19 -1
  47. package/src/constants.ts +28 -14
  48. package/src/index.ts +12 -1
  49. package/src/protocols/index.ts +39 -40
  50. package/src/protocols/v2/ble-frame-writer.ts +77 -0
  51. package/src/protocols/v2/constants.ts +1 -0
  52. package/src/protocols/v2/crc8.ts +1 -1
  53. package/src/protocols/v2/decode.ts +71 -37
  54. package/src/protocols/v2/encode.ts +2 -28
  55. package/src/protocols/v2/errors.ts +25 -0
  56. package/src/protocols/v2/frame-assembler.ts +6 -4
  57. package/src/protocols/v2/index.ts +2 -1
  58. package/src/protocols/v2/link-manager.ts +209 -0
  59. package/src/protocols/v2/sequence-cursor.ts +10 -0
  60. package/src/protocols/v2/session.ts +157 -200
  61. package/src/protocols/v2/usb-transport-base.ts +213 -0
  62. package/src/serialization/protobuf/decode.ts +4 -1
  63. package/src/types/messages.ts +464 -142
  64. package/src/types/transport.ts +14 -3
  65. package/src/utils/transportLog.ts +102 -0
  66. package/dist/protocols/v2/debug.d.ts +0 -13
  67. package/dist/protocols/v2/debug.d.ts.map +0 -1
  68. 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
+ }
@@ -0,0 +1,10 @@
1
+ import { nextProtoSeq } from './encode';
2
+
3
+ export class ProtocolV2SequenceCursor {
4
+ private current = 0;
5
+
6
+ next(): number {
7
+ this.current = nextProtoSeq(this.current);
8
+ return this.current;
9
+ }
10
+ }