@onekeyfe/hd-transport-lowlevel 1.2.0-alpha.1 → 1.2.0-alpha.100

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.
@@ -0,0 +1,42 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ const { getProtocolV1SendOptions, shouldLogFirmwareUploadProgress } = require('../src');
3
+
4
+ describe('firmware upload progress logging', () => {
5
+ test('按 5% 进度限流打印', () => {
6
+ expect(
7
+ shouldLogFirmwareUploadProgress({
8
+ percent: 9,
9
+ lastLoggedPercent: 5,
10
+ now: 5_000,
11
+ lastLoggedAt: 0,
12
+ })
13
+ ).toBe(false);
14
+
15
+ expect(
16
+ shouldLogFirmwareUploadProgress({
17
+ percent: 10,
18
+ lastLoggedPercent: 5,
19
+ now: 5_000,
20
+ lastLoggedAt: 0,
21
+ })
22
+ ).toBe(true);
23
+ });
24
+
25
+ test('进度不足 5% 时最长每 10 秒打印一次心跳', () => {
26
+ expect(
27
+ shouldLogFirmwareUploadProgress({
28
+ percent: 7,
29
+ lastLoggedPercent: 5,
30
+ now: 10_000,
31
+ lastLoggedAt: 0,
32
+ })
33
+ ).toBe(true);
34
+ });
35
+ });
36
+
37
+ describe('firmware upload write mode', () => {
38
+ test('固件上传使用带响应写入,普通命令保持默认模式', () => {
39
+ expect(getProtocolV1SendOptions('FirmwareUpload')).toEqual({ withoutResponse: false });
40
+ expect(getProtocolV1SendOptions('Initialize')).toBeUndefined();
41
+ });
42
+ });
@@ -29,21 +29,25 @@ const protocolV1Schema = {
29
29
 
30
30
  const protocolV2Schema = {
31
31
  nested: {
32
- GetProtoVersion: {
32
+ ProtocolInfoRequest: {
33
33
  fields: {},
34
34
  },
35
- ProtoVersion: {
35
+ ProtocolInfo: {
36
36
  fields: {
37
- major_version: {
37
+ version: {
38
38
  type: 'uint32',
39
39
  id: 1,
40
40
  },
41
- minor_version: {
41
+ supported_messages: {
42
+ rule: 'repeated',
42
43
  type: 'uint32',
43
44
  id: 2,
45
+ options: {
46
+ packed: false,
47
+ },
44
48
  },
45
- patch_version: {
46
- type: 'uint32',
49
+ protobuf_definition: {
50
+ type: 'string',
47
51
  id: 3,
48
52
  },
49
53
  },
@@ -66,8 +70,8 @@ const protocolV2Schema = {
66
70
  },
67
71
  MessageType: {
68
72
  values: {
69
- MessageType_GetProtoVersion: 60200,
70
- MessageType_ProtoVersion: 60201,
73
+ MessageType_ProtocolInfoRequest: 60200,
74
+ MessageType_ProtocolInfo: 60201,
71
75
  MessageType_Ping: 60206,
72
76
  MessageType_Success: 60207,
73
77
  },
@@ -118,6 +122,49 @@ const splitFrame = (frame, index) => [
118
122
  ];
119
123
 
120
124
  describe('LowlevelTransport protocol framing', () => {
125
+ test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
126
+ const plugin = createPlugin({ devices: [], responses: [] });
127
+ const lowlevel = configureTransport(plugin);
128
+ const events = [];
129
+ lowlevel.probeProtocolV1 = jest.fn().mockImplementation(() => {
130
+ events.push('probe-v1');
131
+ return Promise.resolve(false);
132
+ });
133
+ lowlevel.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
134
+ events.push('reset');
135
+ return Promise.resolve();
136
+ });
137
+ lowlevel.probeProtocolV2 = jest.fn().mockImplementation(() => {
138
+ events.push('probe-v2');
139
+ return Promise.resolve(true);
140
+ });
141
+
142
+ await expect(lowlevel.detectProtocol('pro-lowlevel', undefined, 'V1')).resolves.toBe('V2');
143
+
144
+ expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
145
+ expect(lowlevel.getProtocolType('pro-lowlevel')).toBe('V2');
146
+ });
147
+
148
+ test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
149
+ const lowlevel = new LowlevelTransport();
150
+ const invalidateAllLinks = jest.fn().mockResolvedValue(undefined);
151
+ lowlevel.protocolV2Links.invalidateAllLinks = invalidateAllLinks;
152
+
153
+ lowlevel.configureProtocolV2(protocolV2Schema);
154
+ lowlevel.configureProtocolV2(protocolV2Schema);
155
+
156
+ expect(invalidateAllLinks).not.toHaveBeenCalled();
157
+
158
+ lowlevel.configureProtocolV2({
159
+ nested: {
160
+ ...protocolV2Schema.nested,
161
+ ExtraMessage: { fields: {} },
162
+ },
163
+ });
164
+
165
+ expect(invalidateAllLinks).toHaveBeenCalledWith('Protocol V2 schema reconfigured');
166
+ });
167
+
121
168
  test('keeps Protocol V1 raw notification chunks compatible', async () => {
122
169
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
123
170
  message: 'ok',
@@ -138,6 +185,23 @@ describe('LowlevelTransport protocol framing', () => {
138
185
  });
139
186
  });
140
187
 
188
+ test('uses the Protocol V2 BLE writer with the lowlevel compatibility packet size', async () => {
189
+ const plugin = createPlugin({ devices: [], responses: [] });
190
+ const lowlevel = configureTransport(plugin);
191
+ const context = {
192
+ messageName: 'Ping',
193
+ timeoutMs: 1000,
194
+ highThroughput: false,
195
+ generation: 1,
196
+ signal: new AbortController().signal,
197
+ };
198
+
199
+ await lowlevel.writeProtocolV2Frame('pro2-id', new Uint8Array(130), context, jest.fn());
200
+
201
+ expect(plugin.send).toHaveBeenCalledTimes(3);
202
+ expect(plugin.send.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([64, 64, 2]);
203
+ });
204
+
141
205
  test('rejects calls before protocol detection', async () => {
142
206
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
143
207
  message: 'ok',
@@ -162,13 +226,13 @@ describe('LowlevelTransport protocol framing', () => {
162
226
  );
163
227
  const callResponse = ProtocolV2.encodeFrame(
164
228
  schemas,
165
- 'ProtoVersion',
229
+ 'ProtocolInfo',
166
230
  {
167
- major_version: 2,
168
- minor_version: 1,
169
- patch_version: 3,
231
+ version: 1,
232
+ supported_messages: [60200, 60201, 60206, 60207],
233
+ protobuf_definition: 'onekey-protocol-v2',
170
234
  },
171
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
235
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: 2 }
172
236
  );
173
237
  const plugin = createPlugin({
174
238
  devices: [{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
@@ -183,15 +247,19 @@ describe('LowlevelTransport protocol framing', () => {
183
247
  uuid: 'pro2-id',
184
248
  protocolType: 'V2',
185
249
  });
186
- await expect(lowlevel.call('pro2-id', 'GetProtoVersion', {})).resolves.toEqual({
187
- type: 'ProtoVersion',
250
+ await expect(lowlevel.call('pro2-id', 'ProtocolInfoRequest', {})).resolves.toEqual({
251
+ type: 'ProtocolInfo',
188
252
  message: {
189
- major_version: 2,
190
- minor_version: 1,
191
- patch_version: 3,
253
+ version: 1,
254
+ supported_messages: [60200, 60201, 60206, 60207],
255
+ protobuf_definition: 'onekey-protocol-v2',
192
256
  },
193
257
  });
194
258
  expect(plugin.send).toHaveBeenCalled();
259
+ const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
260
+ Number.parseInt(hex.slice(12, 14), 16)
261
+ );
262
+ expect(sentSeqs).toEqual([1, 2]);
195
263
  });
196
264
 
197
265
  test('falls back to Protocol V2 probe for unnamed Protocol V2 devices', async () => {
@@ -214,6 +282,97 @@ describe('LowlevelTransport protocol framing', () => {
214
282
  expect(lowlevel.getProtocolType('unknown-pro2-id')).toBe('V2');
215
283
  });
216
284
 
285
+ test('retains the Protocol V2 hint and sequence cursor across release and reacquire', async () => {
286
+ const probeResponse = ProtocolV2.encodeFrame(
287
+ schemas,
288
+ 'Success',
289
+ { message: 'ok' },
290
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
291
+ );
292
+ const plugin = createPlugin({
293
+ devices: [{ id: 'reconnect-pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
294
+ responses: [bytesToHex(probeResponse), bytesToHex(probeResponse)],
295
+ });
296
+ const lowlevel = configureTransport(plugin);
297
+
298
+ await lowlevel.enumerate();
299
+ await expect(lowlevel.acquire({ uuid: 'reconnect-pro2-id' })).resolves.toEqual({
300
+ uuid: 'reconnect-pro2-id',
301
+ protocolType: 'V2',
302
+ });
303
+ await lowlevel.release('reconnect-pro2-id');
304
+ await expect(lowlevel.acquire({ uuid: 'reconnect-pro2-id' })).resolves.toEqual({
305
+ uuid: 'reconnect-pro2-id',
306
+ protocolType: 'V2',
307
+ });
308
+
309
+ const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
310
+ Number.parseInt(hex.slice(12, 14), 16)
311
+ );
312
+ expect(sentSeqs).toEqual([1, 2]);
313
+ });
314
+
315
+ test('reuses the active generation when Core acquires the same BLE connection again', async () => {
316
+ const responses = [1, 2, 3, 4].map(seq =>
317
+ bytesToHex(
318
+ ProtocolV2.encodeFrame(
319
+ schemas,
320
+ 'Success',
321
+ { message: 'ok' },
322
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq }
323
+ )
324
+ )
325
+ );
326
+ const plugin = createPlugin({
327
+ devices: [{ id: 'repeated-acquire-id', name: 'OneKey Pro 2', commType: 'ble' }],
328
+ responses,
329
+ });
330
+ const lowlevel = configureTransport(plugin);
331
+
332
+ await expect(
333
+ lowlevel.acquire({ uuid: 'repeated-acquire-id', expectedProtocol: 'V2' })
334
+ ).resolves.toEqual({
335
+ uuid: 'repeated-acquire-id',
336
+ protocolType: 'V2',
337
+ });
338
+ await lowlevel.call('repeated-acquire-id', 'Ping', { message: 'first-acquire' });
339
+ await expect(
340
+ lowlevel.acquire({ uuid: 'repeated-acquire-id', expectedProtocol: 'V2' })
341
+ ).resolves.toEqual({
342
+ uuid: 'repeated-acquire-id',
343
+ protocolType: 'V2',
344
+ });
345
+ await lowlevel.call('repeated-acquire-id', 'Ping', { message: 'second-acquire' });
346
+
347
+ const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
348
+ Number.parseInt(hex.slice(12, 14), 16)
349
+ );
350
+ expect(sentSeqs).toEqual([1, 2, 3, 4]);
351
+ });
352
+
353
+ test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
354
+ const probeResponse = ProtocolV2.encodeFrame(
355
+ schemas,
356
+ 'Success',
357
+ { message: 'ok' },
358
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
359
+ );
360
+ const plugin = createPlugin({
361
+ devices: [{ id: 'bootloader-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
362
+ responses: [bytesToHex(probeResponse)],
363
+ });
364
+ const lowlevel = configureTransport(plugin);
365
+
366
+ await expect(
367
+ lowlevel.acquire({ uuid: 'bootloader-v2-id', expectedProtocol: 'V2' })
368
+ ).resolves.toEqual({
369
+ uuid: 'bootloader-v2-id',
370
+ protocolType: 'V2',
371
+ });
372
+ expect(plugin.send).toHaveBeenCalledTimes(1);
373
+ expect(plugin.receive).toHaveBeenCalledTimes(1);
374
+ });
375
+
217
376
  test('resets the lowlevel connection before probing Protocol V2 after a V1 timeout', async () => {
218
377
  const probeResponse = ProtocolV2.encodeFrame(
219
378
  schemas,
@@ -252,6 +411,57 @@ describe('LowlevelTransport protocol framing', () => {
252
411
  expect(plugin.connect).toHaveBeenCalledTimes(2);
253
412
  });
254
413
 
414
+ test('disconnects a tainted Protocol V2 link after a response timeout', async () => {
415
+ const probeResponse = ProtocolV2.encodeFrame(
416
+ schemas,
417
+ 'Success',
418
+ { message: 'ok' },
419
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
420
+ );
421
+ const plugin = createPlugin({
422
+ devices: [{ id: 'timeout-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
423
+ responses: [bytesToHex(probeResponse)],
424
+ });
425
+ let receiveCount = 0;
426
+ plugin.receive.mockImplementation(() => {
427
+ receiveCount += 1;
428
+ return receiveCount === 1
429
+ ? Promise.resolve(bytesToHex(probeResponse))
430
+ : new Promise(() => {});
431
+ });
432
+ const lowlevel = configureTransport(plugin);
433
+
434
+ await lowlevel.acquire({ uuid: 'timeout-v2-id', expectedProtocol: 'V2' });
435
+ plugin.disconnect.mockClear();
436
+ await expect(
437
+ lowlevel.call('timeout-v2-id', 'Ping', { message: 'timeout' }, { timeoutMs: 10 })
438
+ ).rejects.toThrow('Lowlevel response timeout after 10ms for Ping');
439
+
440
+ expect(plugin.disconnect).toHaveBeenCalledWith('timeout-v2-id');
441
+ });
442
+
443
+ test('preserves an undefined business timeout outside explicit probes', async () => {
444
+ const plugin = createPlugin({
445
+ devices: [{ id: 'v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
446
+ responses: [],
447
+ });
448
+ const lowlevel = configureTransport(plugin);
449
+ lowlevel.deviceProtocol.set('v2-id', 'V2');
450
+ const linkCall = jest
451
+ .spyOn(lowlevel.protocolV2Links, 'call')
452
+ .mockResolvedValue({ type: 'Success', message: {} });
453
+
454
+ await lowlevel.call('v2-id', 'Ping', { message: 'no-business-timeout' });
455
+
456
+ expect(linkCall).toHaveBeenCalledWith(
457
+ 'v2-id',
458
+ expect.any(Function),
459
+ 'Ping',
460
+ { message: 'no-business-timeout' },
461
+ undefined
462
+ );
463
+ });
464
+
255
465
  test('verifies expected Protocol V1 instead of trusting the requested protocol', async () => {
256
466
  const plugin = createPlugin({
257
467
  devices: [{ id: 'v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
package/dist/index.d.ts CHANGED
@@ -5,8 +5,18 @@ import EventEmitter from 'events';
5
5
  type LowLevelAcquireInput = {
6
6
  uuid: string;
7
7
  expectedProtocol?: ProtocolType;
8
+ protocolHint?: ProtocolType;
8
9
  };
9
10
 
11
+ declare function shouldLogFirmwareUploadProgress({ percent, lastLoggedPercent, now, lastLoggedAt, }: {
12
+ percent: number;
13
+ lastLoggedPercent: number;
14
+ now: number;
15
+ lastLoggedAt: number;
16
+ }): boolean;
17
+ declare function getProtocolV1SendOptions(name: string): {
18
+ withoutResponse: boolean;
19
+ } | undefined;
10
20
  declare class LowlevelTransport {
11
21
  _messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
12
22
  _messagesV2: ReturnType<typeof transport__default.parseConfigure> | undefined;
@@ -17,6 +27,10 @@ declare class LowlevelTransport {
17
27
  private deviceProtocol;
18
28
  private deviceProtocolHints;
19
29
  private protocolV2Assemblers;
30
+ private protocolV2Generations;
31
+ private connectedDevices;
32
+ private protocolV2Links;
33
+ private protocolV2SchemaConfiguration;
20
34
  getProtocolType(path: string): ProtocolType | undefined;
21
35
  init(logger: any, emitter: EventEmitter, plugin: LowlevelTransportSharedPlugin): void;
22
36
  configure(signedData: any): void;
@@ -43,7 +57,9 @@ declare class LowlevelTransport {
43
57
  private readProtocolV2Frame;
44
58
  private writeProtocolV2Frame;
45
59
  private callProtocolV2;
60
+ private createProtocolV2Adapter;
61
+ private advanceProtocolV2Generation;
46
62
  cancel(): void;
47
63
  }
48
64
 
49
- export { LowlevelTransport as default };
65
+ export { LowlevelTransport as default, getProtocolV1SendOptions, shouldLogFirmwareUploadProgress };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAqBpD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAInC,MAAM;IAIA,SAAS;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAuBnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAapB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAmClB,cAAc;IAuC5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YAsDd,yBAAyB;YA0BzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAOpB,cAAc;IAsC5B,MAAM;CAGP"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAYN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,YAAY,EAEZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAUpD,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,iBAAiB,EACjB,GAAG,EACH,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB,WAMA;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM;;cAEpD;AAcD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,eAAe,CA6BpB;IAEH,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAiBnC,MAAM;IAIA,SAAS;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAgCnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAgBpB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAsBlB,cAAc;IA6E5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YA2Cd,yBAAyB;YAiCzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAgBpB,cAAc;IAwB5B,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,2BAA2B;IAMnC,MAAM;CAGP"}