@onekeyfe/hd-transport 1.2.8-alpha.113 → 1.2.8-alpha.114
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/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +7 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -186
- package/dist/protocols/v2/session.d.ts +1 -4
- package/dist/protocols/v2/session.d.ts.map +1 -1
- package/dist/types/transport.d.ts +1 -5
- package/dist/types/transport.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +0 -7
- package/src/index.ts +0 -1
- package/src/protocols/v2/session.ts +63 -300
- package/src/types/transport.ts +6 -19
- package/__tests__/protocol-v2-file-write-pipeline.test.js +0 -417
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
|
|
3
|
-
PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW,
|
|
4
3
|
PROTOCOL_V2_PACKET_SRC_COMMAND,
|
|
5
4
|
} from '../../constants';
|
|
6
5
|
import { ProtocolV2FrameAssembler, concatUint8Arrays } from './frame-assembler';
|
|
@@ -16,11 +15,7 @@ import * as check from '../../utils/highlevel-checks';
|
|
|
16
15
|
import { LogBlockCommand } from '../../utils/logBlockCommand';
|
|
17
16
|
|
|
18
17
|
import type { Root } from 'protobufjs/light';
|
|
19
|
-
import type {
|
|
20
|
-
MessageFromOneKey,
|
|
21
|
-
ProtocolV2FileWritePipeline,
|
|
22
|
-
TransportWriteMetrics,
|
|
23
|
-
} from '../../types';
|
|
18
|
+
import type { MessageFromOneKey, TransportWriteMetrics } from '../../types';
|
|
24
19
|
|
|
25
20
|
export * from './errors';
|
|
26
21
|
|
|
@@ -67,7 +62,6 @@ export type ProtocolV2CallOptions = {
|
|
|
67
62
|
returnAfterWrite?: boolean;
|
|
68
63
|
onResponseAfterWrite?: (response: MessageFromOneKey) => void;
|
|
69
64
|
writeWithResponse?: boolean;
|
|
70
|
-
fileWritePipeline?: ProtocolV2FileWritePipeline;
|
|
71
65
|
};
|
|
72
66
|
|
|
73
67
|
export { concatUint8Arrays, ProtocolV2FrameAssembler };
|
|
@@ -220,27 +214,6 @@ export async function withProtocolTimeout<T>(
|
|
|
220
214
|
}
|
|
221
215
|
}
|
|
222
216
|
|
|
223
|
-
function readFileWriteChunk(request: Record<string, unknown>) {
|
|
224
|
-
const file = request.file as
|
|
225
|
-
| { path?: unknown; offset?: unknown; total_size?: unknown; data?: { byteLength?: unknown } }
|
|
226
|
-
| undefined;
|
|
227
|
-
const offset = Number(file?.offset);
|
|
228
|
-
const totalSize = Number(file?.total_size);
|
|
229
|
-
const length = Number(file?.data?.byteLength);
|
|
230
|
-
if (
|
|
231
|
-
typeof file?.path !== 'string' ||
|
|
232
|
-
!Number.isSafeInteger(offset) ||
|
|
233
|
-
offset < 0 ||
|
|
234
|
-
!Number.isSafeInteger(totalSize) ||
|
|
235
|
-
!Number.isSafeInteger(length) ||
|
|
236
|
-
length <= 0 ||
|
|
237
|
-
offset + length > totalSize
|
|
238
|
-
) {
|
|
239
|
-
throw new Error('Protocol V2 file write pipeline chunk is invalid');
|
|
240
|
-
}
|
|
241
|
-
return { path: file.path, offset, end: offset + length, totalSize };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
217
|
export class ProtocolV2Session {
|
|
245
218
|
private readonly options: ProtocolV2SessionOptions;
|
|
246
219
|
|
|
@@ -325,9 +298,6 @@ export class ProtocolV2Session {
|
|
|
325
298
|
data: Record<string, unknown>,
|
|
326
299
|
callOptions: ProtocolV2CallOptions
|
|
327
300
|
): Promise<MessageFromOneKey> {
|
|
328
|
-
if (callOptions.fileWritePipeline) {
|
|
329
|
-
return this.executeFileWritePipeline(name, data, callOptions, callOptions.fileWritePipeline);
|
|
330
|
-
}
|
|
331
301
|
const {
|
|
332
302
|
schemas,
|
|
333
303
|
router,
|
|
@@ -406,8 +376,68 @@ export class ProtocolV2Session {
|
|
|
406
376
|
const rxFrame = await readFrame(baseCallContext);
|
|
407
377
|
if (abortController.signal.aborted) break;
|
|
408
378
|
|
|
409
|
-
|
|
410
|
-
|
|
379
|
+
let header: ReturnType<typeof ProtocolV2.inspectFrameHeader>;
|
|
380
|
+
try {
|
|
381
|
+
header = ProtocolV2.inspectFrameHeader(rxFrame);
|
|
382
|
+
} catch (cause) {
|
|
383
|
+
throw new ProtocolV2LinkError(
|
|
384
|
+
'frame',
|
|
385
|
+
`Protocol V2 frame validation failed: ${getErrorMessage(cause)}`,
|
|
386
|
+
cause
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
if (header.router !== router) {
|
|
390
|
+
throw new ProtocolV2LinkError(
|
|
391
|
+
'router',
|
|
392
|
+
`Protocol V2 router mismatch: expected ${router}, got ${header.router}`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
if (header.packetSrc !== packetSrc) {
|
|
396
|
+
throw new ProtocolV2LinkError(
|
|
397
|
+
'packet-source',
|
|
398
|
+
`Protocol V2 packet source mismatch: expected ${packetSrc}, got ${header.packetSrc}`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let isAck: boolean;
|
|
403
|
+
try {
|
|
404
|
+
isAck = ProtocolV2.isAckFrame(rxFrame);
|
|
405
|
+
} catch (cause) {
|
|
406
|
+
throw new ProtocolV2LinkError(
|
|
407
|
+
'frame',
|
|
408
|
+
`Protocol V2 ACK validation failed: ${getErrorMessage(cause)}`,
|
|
409
|
+
cause
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
if (isAck) {
|
|
413
|
+
if (header.seq !== protoSeq) {
|
|
414
|
+
throw new ProtocolV2LinkError(
|
|
415
|
+
'ack-sequence',
|
|
416
|
+
`Protocol V2 ACK sequence mismatch: expected ${protoSeq}, got ${header.seq}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
let decoded: ReturnType<typeof ProtocolV2.decodeFrame>;
|
|
421
|
+
try {
|
|
422
|
+
decoded = ProtocolV2.decodeFrame(schemas, rxFrame);
|
|
423
|
+
} catch (cause) {
|
|
424
|
+
throw new ProtocolV2LinkError(
|
|
425
|
+
'frame',
|
|
426
|
+
`Protocol V2 frame decode failed: ${getErrorMessage(cause)}`,
|
|
427
|
+
cause
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
// Firmware owns one TX sequence across all channels and packet sources,
|
|
431
|
+
// so frames routed elsewhere create valid gaps in this session.
|
|
432
|
+
if (this.lastResponseSequence === decoded.seq) {
|
|
433
|
+
throw new ProtocolV2LinkError(
|
|
434
|
+
'response-sequence',
|
|
435
|
+
`Protocol V2 duplicate response sequence: ${decoded.seq}`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
this.lastResponseSequence = decoded.seq;
|
|
439
|
+
|
|
440
|
+
const response = check.call(decoded);
|
|
411
441
|
const { pendingResponseAfterWrite } = this;
|
|
412
442
|
let belongsToWriteOnlyCall = false;
|
|
413
443
|
if (pendingResponseAfterWrite) {
|
|
@@ -456,273 +486,6 @@ export class ProtocolV2Session {
|
|
|
456
486
|
}
|
|
457
487
|
);
|
|
458
488
|
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Validates one received frame for this session. Returns undefined for an ACK of one of
|
|
462
|
-
* `ackSequences`, otherwise the decoded response.
|
|
463
|
-
*/
|
|
464
|
-
private parseResponseFrame(
|
|
465
|
-
rxFrame: Uint8Array,
|
|
466
|
-
ackSequences: readonly number[]
|
|
467
|
-
): MessageFromOneKey | undefined {
|
|
468
|
-
const { schemas, router, packetSrc = PROTOCOL_V2_PACKET_SRC_COMMAND } = this.options;
|
|
469
|
-
let header: ReturnType<typeof ProtocolV2.inspectFrameHeader>;
|
|
470
|
-
try {
|
|
471
|
-
header = ProtocolV2.inspectFrameHeader(rxFrame);
|
|
472
|
-
} catch (cause) {
|
|
473
|
-
throw new ProtocolV2LinkError(
|
|
474
|
-
'frame',
|
|
475
|
-
`Protocol V2 frame validation failed: ${getErrorMessage(cause)}`,
|
|
476
|
-
cause
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
if (header.router !== router) {
|
|
480
|
-
throw new ProtocolV2LinkError(
|
|
481
|
-
'router',
|
|
482
|
-
`Protocol V2 router mismatch: expected ${router}, got ${header.router}`
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
if (header.packetSrc !== packetSrc) {
|
|
486
|
-
throw new ProtocolV2LinkError(
|
|
487
|
-
'packet-source',
|
|
488
|
-
`Protocol V2 packet source mismatch: expected ${packetSrc}, got ${header.packetSrc}`
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
let isAck: boolean;
|
|
493
|
-
try {
|
|
494
|
-
isAck = ProtocolV2.isAckFrame(rxFrame);
|
|
495
|
-
} catch (cause) {
|
|
496
|
-
throw new ProtocolV2LinkError(
|
|
497
|
-
'frame',
|
|
498
|
-
`Protocol V2 ACK validation failed: ${getErrorMessage(cause)}`,
|
|
499
|
-
cause
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
if (isAck) {
|
|
503
|
-
if (!ackSequences.includes(header.seq)) {
|
|
504
|
-
throw new ProtocolV2LinkError(
|
|
505
|
-
'ack-sequence',
|
|
506
|
-
`Protocol V2 ACK sequence mismatch: expected ${ackSequences.join('|')}, got ${header.seq}`
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
return undefined;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
let decoded: ReturnType<typeof ProtocolV2.decodeFrame>;
|
|
513
|
-
try {
|
|
514
|
-
decoded = ProtocolV2.decodeFrame(schemas, rxFrame);
|
|
515
|
-
} catch (cause) {
|
|
516
|
-
throw new ProtocolV2LinkError(
|
|
517
|
-
'frame',
|
|
518
|
-
`Protocol V2 frame decode failed: ${getErrorMessage(cause)}`,
|
|
519
|
-
cause
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
// Firmware owns one TX sequence across all channels and packet sources,
|
|
523
|
-
// so frames routed elsewhere create valid gaps in this session.
|
|
524
|
-
if (this.lastResponseSequence === decoded.seq) {
|
|
525
|
-
throw new ProtocolV2LinkError(
|
|
526
|
-
'response-sequence',
|
|
527
|
-
`Protocol V2 duplicate response sequence: ${decoded.seq}`
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
this.lastResponseSequence = decoded.seq;
|
|
531
|
-
return check.call(decoded);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Writes one file as ordered FilesystemFileWrite chunks while keeping up to
|
|
536
|
-
* PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW chunks in flight. Firmware still processes and
|
|
537
|
-
* answers every chunk in order, so responses are matched to chunks by position. Upstream
|
|
538
|
-
* bootloaders append at their own cursor without checking the request offset, so the echoed
|
|
539
|
-
* offset is what exposes a lost chunk. A device Failure, or `next` returning undefined, stops
|
|
540
|
-
* sending; the remaining in-flight responses are drained so the link stays usable. The call
|
|
541
|
-
* then returns the Failure like a serial call, or the last confirmation, which the caller
|
|
542
|
-
* checks for completeness.
|
|
543
|
-
*/
|
|
544
|
-
private async executeFileWritePipeline(
|
|
545
|
-
name: string,
|
|
546
|
-
data: Record<string, unknown>,
|
|
547
|
-
callOptions: ProtocolV2CallOptions,
|
|
548
|
-
pipeline: ProtocolV2FileWritePipeline
|
|
549
|
-
): Promise<MessageFromOneKey> {
|
|
550
|
-
if (name !== 'FilesystemFileWrite') {
|
|
551
|
-
throw new Error(`Protocol V2 file write pipeline does not support ${name}`);
|
|
552
|
-
}
|
|
553
|
-
if (this.pendingResponseAfterWrite) {
|
|
554
|
-
throw new Error('Protocol V2 file write pipeline cannot start while a response is pending');
|
|
555
|
-
}
|
|
556
|
-
const {
|
|
557
|
-
schemas,
|
|
558
|
-
router,
|
|
559
|
-
packetSrc = PROTOCOL_V2_PACKET_SRC_COMMAND,
|
|
560
|
-
maxFrameBytes,
|
|
561
|
-
prepareCall,
|
|
562
|
-
writeFrame,
|
|
563
|
-
readFrame,
|
|
564
|
-
logger,
|
|
565
|
-
logPrefix = 'ProtocolV2',
|
|
566
|
-
createTimeoutError,
|
|
567
|
-
generation = 0,
|
|
568
|
-
} = this.options;
|
|
569
|
-
const timeoutMs =
|
|
570
|
-
callOptions.timeoutMs && callOptions.timeoutMs > 0
|
|
571
|
-
? callOptions.timeoutMs
|
|
572
|
-
: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
|
|
573
|
-
const abortController = new AbortController();
|
|
574
|
-
const context: ProtocolV2CallContext = {
|
|
575
|
-
messageName: name,
|
|
576
|
-
timeoutMs,
|
|
577
|
-
highThroughput: true,
|
|
578
|
-
...(callOptions.writeWithResponse === undefined
|
|
579
|
-
? {}
|
|
580
|
-
: { writeWithResponse: callOptions.writeWithResponse }),
|
|
581
|
-
generation,
|
|
582
|
-
signal: abortController.signal,
|
|
583
|
-
};
|
|
584
|
-
// One file can take far longer than one chunk, so the watchdog bounds each step instead.
|
|
585
|
-
const withStepTimeout = <T>(promise: Promise<T>, stepTimeoutMs = timeoutMs) =>
|
|
586
|
-
withProtocolTimeout(
|
|
587
|
-
promise,
|
|
588
|
-
stepTimeoutMs,
|
|
589
|
-
() =>
|
|
590
|
-
createTimeoutError
|
|
591
|
-
? createTimeoutError(name, timeoutMs)
|
|
592
|
-
: new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
|
|
593
|
-
() => {
|
|
594
|
-
abortController.abort();
|
|
595
|
-
}
|
|
596
|
-
);
|
|
597
|
-
// ACKs and skipped frames must not extend the wait for an in-flight chunk's response.
|
|
598
|
-
let progressDeadline = 0;
|
|
599
|
-
const markProgress = () => {
|
|
600
|
-
progressDeadline = Date.now() + timeoutMs;
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
const inFlight: Array<{ seq: number; path: string; offset: number; end: number }> = [];
|
|
604
|
-
let firstRequest: Record<string, unknown> | undefined = data;
|
|
605
|
-
let path: string | undefined;
|
|
606
|
-
let totalSize: number | undefined;
|
|
607
|
-
let nextOffset: number | undefined;
|
|
608
|
-
let sourceDone = false;
|
|
609
|
-
let failure: MessageFromOneKey | undefined;
|
|
610
|
-
let lastConfirmed: MessageFromOneKey | undefined;
|
|
611
|
-
|
|
612
|
-
const sendNextChunk = async () => {
|
|
613
|
-
const request = firstRequest ?? (await pipeline.next());
|
|
614
|
-
firstRequest = undefined;
|
|
615
|
-
if (!request) {
|
|
616
|
-
sourceDone = true;
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
const chunk = readFileWriteChunk(request);
|
|
620
|
-
if (
|
|
621
|
-
(path !== undefined && chunk.path !== path) ||
|
|
622
|
-
(totalSize !== undefined && chunk.totalSize !== totalSize) ||
|
|
623
|
-
(nextOffset !== undefined && chunk.offset !== nextOffset)
|
|
624
|
-
) {
|
|
625
|
-
throw new Error(
|
|
626
|
-
`Protocol V2 file write pipeline chunk out of order: expected offset ${String(
|
|
627
|
-
nextOffset
|
|
628
|
-
)}, got ${chunk.offset}`
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
|
-
path = chunk.path;
|
|
632
|
-
totalSize = chunk.totalSize;
|
|
633
|
-
nextOffset = chunk.end;
|
|
634
|
-
|
|
635
|
-
const seq = this.sequenceCursor.next();
|
|
636
|
-
const frame = ProtocolV2.encodeFrame(schemas, name, request, {
|
|
637
|
-
packetSrc,
|
|
638
|
-
router,
|
|
639
|
-
seq,
|
|
640
|
-
});
|
|
641
|
-
if (maxFrameBytes !== undefined && frame.length > maxFrameBytes) {
|
|
642
|
-
throw new Error(
|
|
643
|
-
`Protocol V2 frame too large for transport: ${frame.length} > ${maxFrameBytes}`
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
const writeStartedAt = Date.now();
|
|
647
|
-
await withStepTimeout(this.serializeWrite(() => writeFrame(frame, context)));
|
|
648
|
-
inFlight.push({ seq, path: chunk.path, offset: chunk.offset, end: chunk.end });
|
|
649
|
-
markProgress();
|
|
650
|
-
try {
|
|
651
|
-
callOptions.onWriteCompleted?.({
|
|
652
|
-
elapsedMs: Math.max(Date.now() - writeStartedAt, 0),
|
|
653
|
-
frameBytes: frame.byteLength,
|
|
654
|
-
});
|
|
655
|
-
} catch (error) {
|
|
656
|
-
logger?.error?.(`${logPrefix} write metrics callback failed: ${String(error)}`);
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
await withStepTimeout(Promise.resolve().then(() => prepareCall?.(context)));
|
|
661
|
-
while (!sourceDone && inFlight.length < PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW) {
|
|
662
|
-
await sendNextChunk();
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
while (inFlight.length > 0) {
|
|
666
|
-
const rxFrame = await withStepTimeout(
|
|
667
|
-
readFrame(context),
|
|
668
|
-
Math.max(progressDeadline - Date.now(), 1)
|
|
669
|
-
);
|
|
670
|
-
const response = this.parseResponseFrame(
|
|
671
|
-
rxFrame,
|
|
672
|
-
inFlight.map(item => item.seq)
|
|
673
|
-
);
|
|
674
|
-
if (!response) {
|
|
675
|
-
// ACK of an in-flight chunk; its FilesystemFile response follows.
|
|
676
|
-
} else if (response.type === 'FilesystemFile') {
|
|
677
|
-
const [expected] = inFlight;
|
|
678
|
-
const { message } = response;
|
|
679
|
-
if (
|
|
680
|
-
Number(message.processed_byte) !== expected.end ||
|
|
681
|
-
message.path !== expected.path ||
|
|
682
|
-
Number(message.offset) !== expected.offset ||
|
|
683
|
-
Number(message.total_size) !== totalSize
|
|
684
|
-
) {
|
|
685
|
-
throw new ProtocolV2LinkError(
|
|
686
|
-
'frame',
|
|
687
|
-
`Protocol V2 file write response does not match chunk ${expected.offset}-${
|
|
688
|
-
expected.end
|
|
689
|
-
}: got offset ${String(message.offset)} processed_byte ${String(
|
|
690
|
-
message.processed_byte
|
|
691
|
-
)}`
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
inFlight.shift();
|
|
695
|
-
markProgress();
|
|
696
|
-
if (!failure) {
|
|
697
|
-
lastConfirmed = response;
|
|
698
|
-
pipeline.onResponse(response);
|
|
699
|
-
if (!sourceDone) {
|
|
700
|
-
await sendNextChunk();
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
} else if (response.type === 'Failure') {
|
|
704
|
-
inFlight.shift();
|
|
705
|
-
markProgress();
|
|
706
|
-
failure = failure ?? response;
|
|
707
|
-
sourceDone = true;
|
|
708
|
-
} else if (COMMON_TERMINAL_RESPONSE_TYPES.has(response.type)) {
|
|
709
|
-
throw new ProtocolV2LinkError(
|
|
710
|
-
'frame',
|
|
711
|
-
`Protocol V2 file write pipeline received unexpected ${response.type}`
|
|
712
|
-
);
|
|
713
|
-
} else {
|
|
714
|
-
logger?.debug?.(
|
|
715
|
-
`[${logPrefix}] skip unexpected response for ${name}: expected=FilesystemFile got=${response.type}`
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
const terminal = failure ?? lastConfirmed;
|
|
721
|
-
if (!terminal) {
|
|
722
|
-
throw new ProtocolV2LinkError('frame', 'Protocol V2 file write pipeline ended unconfirmed');
|
|
723
|
-
}
|
|
724
|
-
return terminal;
|
|
725
|
-
}
|
|
726
489
|
}
|
|
727
490
|
|
|
728
491
|
export async function probeProtocolV2({
|
package/src/types/transport.ts
CHANGED
|
@@ -34,6 +34,12 @@ export type OneKeyDeviceInfoWithSession = OneKeyUsbDeviceInfo & {
|
|
|
34
34
|
export type OneKeyMobileDeviceInfo = {
|
|
35
35
|
id: string;
|
|
36
36
|
name: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Color letter of the serial number ('A'-'Z') from the Pro2 / Neo BLE advertisement,
|
|
39
|
+
* possibly seen in an earlier scan. Set by the React Native and Electron BLE
|
|
40
|
+
* transports; absent when unknown.
|
|
41
|
+
*/
|
|
42
|
+
bleColorCode?: string;
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
export type OneKeyDeviceInfoBase = {
|
|
@@ -70,19 +76,6 @@ export type TransportWriteMetrics = {
|
|
|
70
76
|
frameBytes: number;
|
|
71
77
|
};
|
|
72
78
|
|
|
73
|
-
/**
|
|
74
|
-
* Supplies the remaining chunks of one file for an ordered FilesystemFileWrite pipeline.
|
|
75
|
-
* The call's request data is the first chunk; `next` returns the following chunk in offset
|
|
76
|
-
* order, or undefined to stop. The call then resolves with the last confirmation (or the
|
|
77
|
-
* device Failure), so the caller must check `processed_byte` for completeness. Callbacks
|
|
78
|
-
* should not throw: an error escapes with chunks still in flight and invalidates the link.
|
|
79
|
-
*/
|
|
80
|
-
export type ProtocolV2FileWritePipeline = {
|
|
81
|
-
next: () => Promise<Record<string, unknown> | undefined>;
|
|
82
|
-
/** Called for each chunk after the device confirms it, in offset order. */
|
|
83
|
-
onResponse: (response: MessageFromOneKey) => void;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
79
|
export type TransportCallOptions = {
|
|
87
80
|
timeoutMs?: number;
|
|
88
81
|
expectedTypes?: string[];
|
|
@@ -96,12 +89,6 @@ export type TransportCallOptions = {
|
|
|
96
89
|
onResponseAfterWrite?: (response: MessageFromOneKey) => void;
|
|
97
90
|
/** Prefer acknowledged BLE characteristic writes for this call when supported. */
|
|
98
91
|
writeWithResponse?: boolean;
|
|
99
|
-
/**
|
|
100
|
-
* Protocol V2 only: keep up to PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW ordered
|
|
101
|
-
* FilesystemFileWrite frames of one file in flight within this call. `timeoutMs`
|
|
102
|
-
* bounds each frame write and each response instead of the whole file.
|
|
103
|
-
*/
|
|
104
|
-
fileWritePipeline?: ProtocolV2FileWritePipeline;
|
|
105
92
|
};
|
|
106
93
|
|
|
107
94
|
type ITransportInitFn = (
|