@onekeyfe/hd-transport 1.2.0-alpha.21 → 1.2.0-alpha.23

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.
@@ -104,14 +104,31 @@ describe('messages', () => {
104
104
  MessageType_DeviceSessionGet: 60606,
105
105
  MessageType_DeviceSession: 60607,
106
106
  MessageType_DeviceSessionAskPin: 60608,
107
+ MessageType_DeviceSessionAskPassphrase: 60609,
107
108
  });
108
109
  expect(v2Messages.nested.DeviceStatusGet).toEqual({ fields: {} });
110
+ expect(v2Messages.nested.ProtocolInfoRequest.fields.eventless_wallet_session).toMatchObject({
111
+ id: 1,
112
+ type: 'bool',
113
+ options: { default: false },
114
+ });
109
115
  expect(v2Messages.nested.DeviceSessionGet.fields.session_id).toMatchObject({
110
116
  id: 1,
111
117
  type: 'bytes',
112
118
  });
113
- expect(v2Messages.nested).not.toHaveProperty('DeviceSessionOpen');
114
- expect(v2Messages.nested).not.toHaveProperty('DeviceSessionResume');
119
+ expect(v2Messages.nested.DeviceSessionPinType.values).toEqual({
120
+ Any: 1,
121
+ Main: 2,
122
+ AttachToPin: 3,
123
+ });
124
+ expect(v2Messages.nested.DeviceSessionErrorCode.values).toEqual({
125
+ DeviceSessionError_None: 0,
126
+ DeviceSessionError_UserCancelled: 1,
127
+ DeviceSessionError_InvalidSession: 2,
128
+ DeviceSessionError_AttachPinUnavailable: 3,
129
+ DeviceSessionError_PassphraseDisabled: 4,
130
+ DeviceSessionError_Busy: 5,
131
+ });
115
132
  expect(v2Messages.nested).not.toHaveProperty('DeviceWalletSelect');
116
133
  expect(v2Messages.nested).not.toHaveProperty('DeviceWalletType');
117
134
  expect(v2Messages.nested).not.toHaveProperty('DeviceHiddenWalletSelect');
@@ -119,7 +136,11 @@ describe('messages', () => {
119
136
  session_id: { id: 1, type: 'bytes' },
120
137
  btc_test_address: { id: 2, type: 'string' },
121
138
  });
122
- expect(v2Messages.nested.DeviceSessionAskPin).toEqual({ fields: {} });
139
+ expect(v2Messages.nested.DeviceSessionAskPin.fields.type).toMatchObject({
140
+ id: 1,
141
+ type: 'DeviceSessionPinType',
142
+ });
143
+ expect(v2Messages.nested.DeviceSessionAskPassphrase).toEqual({ fields: {} });
123
144
  expect(v2Messages.nested.DeviceSessionAskPin_FailureSubCodes.values).toEqual({
124
145
  UserCancel: 1,
125
146
  });
@@ -55,7 +55,12 @@ const rewriteSeq = (frame, seq) => {
55
55
  const createAdapterFactory = sentSeqs => {
56
56
  const adapters = [];
57
57
  let generation = 0;
58
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
58
+ const success = ProtocolV2.encodeFrame(
59
+ schemas,
60
+ 'Success',
61
+ { message: 'ok' },
62
+ { router: 1, packetSrc: 0, seq: 1 }
63
+ );
59
64
 
60
65
  return {
61
66
  adapters,
@@ -108,12 +113,15 @@ describe('ProtocolV2LinkManager', () => {
108
113
 
109
114
  await manager.call('device-a', createAdapter, 'Ping', { message: '1' }, { timeoutMs: 123 });
110
115
 
111
- expect(adapters[0].prepareCall).toHaveBeenCalledWith({
112
- messageName: 'Ping',
113
- timeoutMs: 123,
114
- highVolume: false,
115
- generation: 1,
116
- });
116
+ expect(adapters[0].prepareCall).toHaveBeenCalledWith(
117
+ expect.objectContaining({
118
+ messageName: 'Ping',
119
+ timeoutMs: 123,
120
+ highVolume: false,
121
+ generation: 1,
122
+ signal: expect.any(AbortSignal),
123
+ })
124
+ );
117
125
  });
118
126
 
119
127
  test('invalidates a link-fatal call before the next call creates a new link', async () => {
@@ -200,7 +208,12 @@ describe('ProtocolV2LinkManager', () => {
200
208
  const deviceABlocked = new Promise(resolve => {
201
209
  releaseDeviceA = resolve;
202
210
  });
203
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
211
+ const success = ProtocolV2.encodeFrame(
212
+ schemas,
213
+ 'Success',
214
+ { message: 'ok' },
215
+ { router: 1, packetSrc: 0, seq: 1 }
216
+ );
204
217
  const createAdapter = key => {
205
218
  let requestSeq = 0;
206
219
  return {
@@ -298,7 +311,12 @@ describe('ProtocolV2LinkManager', () => {
298
311
  const writeBlocked = new Promise(resolve => {
299
312
  releaseWrite = resolve;
300
313
  });
301
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
314
+ const success = ProtocolV2.encodeFrame(
315
+ schemas,
316
+ 'Success',
317
+ { message: 'ok' },
318
+ { router: 1, packetSrc: 0, seq: 1 }
319
+ );
302
320
  const adapter = {
303
321
  router: 1,
304
322
  generation: 1,
@@ -2,13 +2,17 @@ const { ProtocolV2 } = require('../src/protocols');
2
2
  const { parseConfigure } = require('../src/serialization/protobuf/messages');
3
3
  const {
4
4
  ProtocolV2FrameAssembler,
5
+ ProtocolV2LinkError,
5
6
  ProtocolV2SequenceCursor,
6
7
  ProtocolV2Session,
7
8
  hexToBytes,
8
9
  probeProtocolV2,
9
10
  } = require('../src/protocols/v2/session');
10
11
  const protocolV2 = require('../src/protocols/v2');
11
- const { PROTOCOL_V2_FRAME_MAX_BYTES } = require('../src/constants');
12
+ const {
13
+ PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
14
+ PROTOCOL_V2_FRAME_MAX_BYTES,
15
+ } = require('../src/constants');
12
16
 
13
17
  const protocolV1Messages = parseConfigure({
14
18
  nested: {
@@ -221,6 +225,7 @@ const schemas = {
221
225
 
222
226
  const rewriteSeq = (frame, seq) => {
223
227
  const copy = new Uint8Array(frame);
228
+ copy[4] = 1;
224
229
  copy[6] = seq;
225
230
  copy[copy.length - 1] = protocolV2.crc8(copy, copy.length - 1);
226
231
  return copy;
@@ -429,7 +434,7 @@ describe('Protocol V2 framing and session', () => {
429
434
  logger,
430
435
  logPrefix: 'ProtocolV2 Test',
431
436
  writeFrame: () => Promise.resolve(),
432
- readFrame: () => Promise.resolve(response),
437
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
433
438
  });
434
439
 
435
440
  await expect(session.call('Ping', { message: 'hello' })).rejects.toThrow();
@@ -451,7 +456,10 @@ describe('Protocol V2 framing and session', () => {
451
456
  const response = ProtocolV2.encodeFrame(schemas, 'Success', {
452
457
  message: 'ok',
453
458
  });
454
- const readFrame = jest.fn().mockResolvedValueOnce(ack).mockResolvedValueOnce(response);
459
+ const readFrame = jest
460
+ .fn()
461
+ .mockResolvedValueOnce(ack)
462
+ .mockResolvedValueOnce(rewriteSeq(response, 1));
455
463
  const session = new ProtocolV2Session({
456
464
  schemas,
457
465
  router: 1,
@@ -468,6 +476,96 @@ describe('Protocol V2 framing and session', () => {
468
476
  expect(readFrame).toHaveBeenCalledTimes(2);
469
477
  });
470
478
 
479
+ test('session accepts a terminal response before an ACK', async () => {
480
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', {
481
+ message: 'ok',
482
+ });
483
+ const session = new ProtocolV2Session({
484
+ schemas,
485
+ router: 1,
486
+ writeFrame: () => Promise.resolve(),
487
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
488
+ });
489
+
490
+ await expect(
491
+ session.call('Ping', { message: 'hello' }, { expectedTypes: ['Success'] })
492
+ ).resolves.toEqual({
493
+ type: 'Success',
494
+ message: {
495
+ message: 'ok',
496
+ },
497
+ });
498
+ });
499
+
500
+ test('session uses the unified response timeout before the first ACK', async () => {
501
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', {
502
+ message: 'ok',
503
+ });
504
+ const readFrame = jest.fn(() => Promise.resolve(rewriteSeq(response, 1)));
505
+ const session = new ProtocolV2Session({
506
+ schemas,
507
+ router: 1,
508
+ writeFrame: () => Promise.resolve(),
509
+ readFrame,
510
+ });
511
+
512
+ await expect(
513
+ session.call(
514
+ 'Ping',
515
+ { message: 'hello' },
516
+ {
517
+ timeoutMs: 12_345,
518
+ expectedTypes: ['Success'],
519
+ }
520
+ )
521
+ ).resolves.toEqual({
522
+ type: 'Success',
523
+ message: {
524
+ message: 'ok',
525
+ },
526
+ });
527
+ expect(readFrame).toHaveBeenCalledWith(expect.objectContaining({ timeoutMs: 12_345 }));
528
+ });
529
+
530
+ test('session rejects an ACK that belongs to another request sequence', async () => {
531
+ const ack = new Uint8Array(8);
532
+ ack[0] = 0x5a;
533
+ ack[1] = 8;
534
+ ack[4] = 1;
535
+ ack[5] = 1;
536
+ ack[6] = 2;
537
+ ack[3] = protocolV2.crc8(ack, 3);
538
+ ack[7] = protocolV2.crc8(ack, 7);
539
+ const session = new ProtocolV2Session({
540
+ schemas,
541
+ router: 1,
542
+ writeFrame: () => Promise.resolve(),
543
+ readFrame: () => Promise.resolve(ack),
544
+ });
545
+
546
+ await expect(session.call('Ping', { message: 'hello' })).rejects.toEqual(
547
+ expect.objectContaining({
548
+ name: 'ProtocolV2LinkError',
549
+ code: 'ack-sequence',
550
+ message: 'Protocol V2 ACK sequence mismatch: expected 1, got 2',
551
+ })
552
+ );
553
+ });
554
+
555
+ test('session rejects a response from another routing channel', async () => {
556
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'wrong route' });
557
+ const session = new ProtocolV2Session({
558
+ schemas,
559
+ router: 1,
560
+ writeFrame: () => Promise.resolve(),
561
+ readFrame: () => Promise.resolve(response),
562
+ });
563
+
564
+ await expect(session.call('Ping', { message: 'hello' })).rejects.toBeInstanceOf(
565
+ ProtocolV2LinkError
566
+ );
567
+ });
568
+
471
569
  test('session rejects BLE frames above its configured frame limit before writing', async () => {
472
570
  const writeFrame = jest.fn().mockResolvedValue(undefined);
473
571
  const response = ProtocolV2.encodeFrame(schemas, 'Success', {
@@ -478,7 +576,7 @@ describe('Protocol V2 framing and session', () => {
478
576
  router: 1,
479
577
  maxFrameBytes: 2048,
480
578
  writeFrame,
481
- readFrame: () => Promise.resolve(response),
579
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
482
580
  });
483
581
 
484
582
  await expect(session.call('Ping', { message: 'x'.repeat(2048) })).rejects.toThrow(
@@ -487,28 +585,40 @@ describe('Protocol V2 framing and session', () => {
487
585
  expect(writeFrame).not.toHaveBeenCalled();
488
586
  });
489
587
 
490
- test('session starts response timeout after the frame is written', async () => {
491
- const response = ProtocolV2.encodeFrame(schemas, 'Success', {
492
- message: 'ok',
493
- });
588
+ test('session watchdog covers a write that never settles', async () => {
589
+ const readFrame = jest.fn();
590
+ let writeSignal;
494
591
  const session = new ProtocolV2Session({
495
592
  schemas,
496
593
  router: 1,
497
- writeFrame: () =>
498
- new Promise(resolve => {
499
- setTimeout(resolve, 30);
500
- }),
501
- readFrame: () => Promise.resolve(response),
594
+ writeFrame: (_frame, context) => {
595
+ writeSignal = context.signal;
596
+ return new Promise(() => {});
597
+ },
598
+ readFrame,
502
599
  });
503
600
 
504
601
  await expect(
505
602
  session.call('Ping', { message: 'hello' }, { timeoutMs: 10, expectedTypes: ['Success'] })
506
- ).resolves.toEqual({
507
- type: 'Success',
508
- message: {
509
- message: 'ok',
510
- },
603
+ ).rejects.toThrow('Protocol V2 response timeout after 10ms for Ping');
604
+ expect(writeSignal.aborted).toBe(true);
605
+ expect(readFrame).not.toHaveBeenCalled();
606
+ });
607
+
608
+ test('session watchdog covers prepareCall before writing', async () => {
609
+ const writeFrame = jest.fn();
610
+ const session = new ProtocolV2Session({
611
+ schemas,
612
+ router: 1,
613
+ prepareCall: () => new Promise(() => {}),
614
+ writeFrame,
615
+ readFrame: jest.fn(),
511
616
  });
617
+
618
+ await expect(session.call('Ping', { message: 'hello' }, { timeoutMs: 10 })).rejects.toThrow(
619
+ 'Protocol V2 response timeout after 10ms for Ping'
620
+ );
621
+ expect(writeFrame).not.toHaveBeenCalled();
512
622
  });
513
623
 
514
624
  test('session logs a device-owned response seq without rejecting the frame', async () => {
@@ -538,6 +648,41 @@ describe('Protocol V2 framing and session', () => {
538
648
  expect(logger.debug).not.toHaveBeenCalled();
539
649
  });
540
650
 
651
+ test('session accepts gaps in the firmware-global response sequence', async () => {
652
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
653
+ const readFrame = jest
654
+ .fn()
655
+ .mockResolvedValueOnce(rewriteSeq(response, 1))
656
+ .mockResolvedValueOnce(rewriteSeq(response, 4));
657
+ const session = new ProtocolV2Session({
658
+ schemas,
659
+ router: 1,
660
+ writeFrame: () => Promise.resolve(),
661
+ readFrame,
662
+ });
663
+
664
+ await session.call('Ping', { message: 'first' });
665
+ await expect(session.call('Ping', { message: 'second' })).resolves.toMatchObject({
666
+ type: 'Success',
667
+ });
668
+ });
669
+
670
+ test('session rejects a duplicate device response sequence', async () => {
671
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
672
+ const readFrame = jest.fn(() => Promise.resolve(rewriteSeq(response, 7)));
673
+ const session = new ProtocolV2Session({
674
+ schemas,
675
+ router: 1,
676
+ writeFrame: () => Promise.resolve(),
677
+ readFrame,
678
+ });
679
+
680
+ await session.call('Ping', { message: 'first' });
681
+ await expect(session.call('Ping', { message: 'second' })).rejects.toThrow(
682
+ 'Protocol V2 duplicate response sequence: 7'
683
+ );
684
+ });
685
+
541
686
  test('session does not log transmit or receive payload details', async () => {
542
687
  const response = ProtocolV2.encodeFrame(schemas, 'Success', {
543
688
  message: 'accepted',
@@ -549,7 +694,7 @@ describe('Protocol V2 framing and session', () => {
549
694
  schemas,
550
695
  router: 1,
551
696
  writeFrame: () => Promise.resolve(),
552
- readFrame: () => Promise.resolve(response),
697
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
553
698
  logger,
554
699
  logPrefix: 'ProtocolV2 Test',
555
700
  });
@@ -575,7 +720,7 @@ describe('Protocol V2 framing and session', () => {
575
720
  schemas,
576
721
  router: 1,
577
722
  writeFrame: () => Promise.resolve(),
578
- readFrame: () => Promise.resolve(response),
723
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
579
724
  logger,
580
725
  });
581
726
 
@@ -600,7 +745,10 @@ describe('Protocol V2 framing and session', () => {
600
745
  const logger = {
601
746
  debug: jest.fn(),
602
747
  };
603
- const readFrame = jest.fn().mockResolvedValueOnce(stale).mockResolvedValueOnce(response);
748
+ const readFrame = jest
749
+ .fn()
750
+ .mockResolvedValueOnce(rewriteSeq(stale, 1))
751
+ .mockResolvedValueOnce(rewriteSeq(response, 2));
604
752
  const session = new ProtocolV2Session({
605
753
  schemas,
606
754
  router: 1,
@@ -637,7 +785,9 @@ describe('Protocol V2 framing and session', () => {
637
785
  const [writtenFrame] = written;
638
786
  const { seq } = protocolV2.decodeFrame(writtenFrame);
639
787
  return Promise.resolve(
640
- readFrame.mock.calls.length === 1 ? rewriteSeq(progress, seq) : rewriteSeq(success, seq)
788
+ readFrame.mock.calls.length === 1
789
+ ? rewriteSeq(progress, seq)
790
+ : rewriteSeq(success, protocolV2.nextProtoSeq(seq))
641
791
  );
642
792
  });
643
793
  const session = new ProtocolV2Session({
@@ -814,7 +964,7 @@ describe('Protocol V2 framing and session', () => {
814
964
  }
815
965
  return Promise.resolve();
816
966
  },
817
- readFrame: () => Promise.resolve(response),
967
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
818
968
  });
819
969
 
820
970
  await expect(session.call('Ping', { message: '1' })).rejects.toThrow('transport write failed');
@@ -883,6 +1033,7 @@ describe('Protocol V2 framing and session', () => {
883
1033
  const writeContexts = [];
884
1034
  const readContexts = [];
885
1035
  const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
1036
+ let responseSeq = 0;
886
1037
  const session = new ProtocolV2Session({
887
1038
  schemas,
888
1039
  router: 1,
@@ -893,18 +1044,65 @@ describe('Protocol V2 framing and session', () => {
893
1044
  },
894
1045
  readFrame: context => {
895
1046
  readContexts.push(context);
896
- return Promise.resolve(response);
1047
+ responseSeq = protocolV2.nextProtoSeq(responseSeq);
1048
+ return Promise.resolve(rewriteSeq(response, responseSeq));
897
1049
  },
898
1050
  });
899
1051
 
900
1052
  await session.call('Ping', { message: 'ping' }, { timeoutMs: 123 });
901
1053
  await session.call('FileWrite', {}, { timeoutMs: 456 });
1054
+ await session.call('Ping', { message: 'default-timeout' });
902
1055
 
903
- expect(writeContexts).toEqual([
904
- { messageName: 'Ping', timeoutMs: 123, highVolume: false, generation: 7 },
905
- { messageName: 'FileWrite', timeoutMs: 456, highVolume: true, generation: 7 },
1056
+ const normalizeContext = ({ signal, ...context }) => ({
1057
+ ...context,
1058
+ signalAborted: signal.aborted,
1059
+ });
1060
+ expect(writeContexts.map(normalizeContext)).toEqual([
1061
+ {
1062
+ messageName: 'Ping',
1063
+ timeoutMs: 123,
1064
+ highVolume: false,
1065
+ generation: 7,
1066
+ signalAborted: false,
1067
+ },
1068
+ {
1069
+ messageName: 'FileWrite',
1070
+ timeoutMs: 456,
1071
+ highVolume: true,
1072
+ generation: 7,
1073
+ signalAborted: false,
1074
+ },
1075
+ {
1076
+ messageName: 'Ping',
1077
+ timeoutMs: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
1078
+ highVolume: false,
1079
+ generation: 7,
1080
+ signalAborted: false,
1081
+ },
1082
+ ]);
1083
+ expect(readContexts.map(normalizeContext)).toEqual([
1084
+ {
1085
+ messageName: 'Ping',
1086
+ timeoutMs: 123,
1087
+ highVolume: false,
1088
+ generation: 7,
1089
+ signalAborted: false,
1090
+ },
1091
+ {
1092
+ messageName: 'FileWrite',
1093
+ timeoutMs: 456,
1094
+ highVolume: true,
1095
+ generation: 7,
1096
+ signalAborted: false,
1097
+ },
1098
+ {
1099
+ messageName: 'Ping',
1100
+ timeoutMs: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
1101
+ highVolume: false,
1102
+ generation: 7,
1103
+ signalAborted: false,
1104
+ },
906
1105
  ]);
907
- expect(readContexts).toEqual(writeContexts);
908
1106
  });
909
1107
 
910
1108
  test('assembler throws and resets on frames with an impossible length field', () => {
@@ -14,4 +14,5 @@ export declare const PROTOCOL_V2_CHANNEL_USB = 0;
14
14
  export declare const PROTOCOL_V2_CHANNEL_BLE_UART = 1;
15
15
  export declare const PROTOCOL_V2_CHANNEL_SOCKET = 2;
16
16
  export declare const PROTOCOL_V2_PACKET_SRC_COMMAND = 0;
17
+ export declare const PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS: number;
17
18
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,KAAO,CAAC;AAG1C,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAG5C,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAGjD,eAAO,MAAM,2BAA2B,QAAqC,CAAC;AAG9E,eAAO,MAAM,+BAA+B,QAAQ,CAAC;AAGrD,eAAO,MAAM,gCAAgC,QAA0C,CAAC;AAKxF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAGhD,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAGvD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AAGxD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,2BAA2B,OAAqC,CAAC;AAM9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAG5C,eAAO,MAAM,8BAA8B,IAAI,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,KAAO,CAAC;AAG1C,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAG5C,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAGjD,eAAO,MAAM,2BAA2B,QAAqC,CAAC;AAG9E,eAAO,MAAM,+BAA+B,QAAQ,CAAC;AAGrD,eAAO,MAAM,gCAAgC,QAA0C,CAAC;AAKxF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAGhD,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAGvD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AAGxD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,2BAA2B,OAAqC,CAAC;AAM9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAG5C,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAQhD,eAAO,MAAM,uCAAuC,QAAgB,CAAC"}