@onekeyfe/hd-core 1.2.0-alpha.81 → 1.2.0-alpha.84

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 (38) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +266 -0
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +36 -0
  3. package/__tests__/protocol-v2.test.ts +181 -2
  4. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  5. package/dist/api/CheckBLEFirmwareRelease.d.ts +1 -1
  6. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  7. package/dist/api/CheckBootloaderRelease.d.ts +1 -1
  8. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  9. package/dist/api/CheckFirmwareRelease.d.ts +1 -1
  10. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +33 -0
  14. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  15. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
  16. package/dist/index.d.ts +4 -3
  17. package/dist/index.js +202 -66
  18. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
  19. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  20. package/dist/types/api/checkBLEFirmwareRelease.d.ts +1 -1
  21. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  22. package/dist/types/api/checkBootloaderRelease.d.ts +1 -1
  23. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  24. package/dist/types/api/checkFirmwareRelease.d.ts +1 -1
  25. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/src/api/CheckAllFirmwareRelease.ts +76 -5
  28. package/src/api/CheckBLEFirmwareRelease.ts +4 -10
  29. package/src/api/CheckBootloaderRelease.ts +1 -8
  30. package/src/api/CheckFirmwareRelease.ts +6 -12
  31. package/src/api/FirmwareUpdateV4.ts +61 -21
  32. package/src/api/firmware/FirmwareUpdatePlan.ts +154 -37
  33. package/src/api/firmware/protocolV2Release.ts +2 -0
  34. package/src/types/api/checkAllFirmwareRelease.ts +1 -0
  35. package/src/types/api/checkBLEFirmwareRelease.ts +1 -1
  36. package/src/types/api/checkBootloaderRelease.ts +1 -1
  37. package/src/types/api/checkFirmwareRelease.ts +1 -1
  38. package/src/utils/deviceFeaturesUtils.ts +2 -2
@@ -333,6 +333,15 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
333
333
  resourceArchive: resourceSource,
334
334
  resourcePreparationRequired: true,
335
335
  targetsToUpdate: ['boot', 'app_v1', 'resource'],
336
+ firmwareUpdatePlan: {
337
+ executor: 'v4',
338
+ platform: 'web',
339
+ artifacts: [
340
+ { artifactId: 'component:boot', target: 'boot' },
341
+ { artifactId: 'component:app_v1', target: 'app_v1' },
342
+ ],
343
+ targetsToUpdate: ['boot', 'app_v1'],
344
+ },
336
345
  firmware: {
337
346
  status: 'required',
338
347
  },
@@ -344,6 +353,260 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
344
353
  expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
345
354
  });
346
355
 
356
+ test('preserves platform and forced firmware targets in the Protocol V2 prepared plan', async () => {
357
+ const method = new CheckAllFirmwareRelease({
358
+ id: 1,
359
+ payload: {
360
+ method: 'checkAllFirmwareRelease',
361
+ firmwareType: EFirmwareType.Universal,
362
+ platform: 'native',
363
+ forceUpdateTargets: ['firmware'],
364
+ },
365
+ });
366
+ method.init();
367
+ method.device = {
368
+ isProtocolV2: () => true,
369
+ features: {
370
+ deviceType: 'pro2',
371
+ serialNo: 'pro2-device-id',
372
+ firmwareVersion: '1.0.0',
373
+ },
374
+ getDeviceState: jest.fn().mockResolvedValue({
375
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
376
+ status: { mode: 'normal' },
377
+ versions: currentVersions,
378
+ }),
379
+ } as unknown as CheckAllFirmwareRelease['device'];
380
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
381
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
382
+
383
+ await expect(method.run()).resolves.toMatchObject({
384
+ targetsToUpdate: ['boot', 'app_v1', 'se02', 'se03', 'resource'],
385
+ firmwareUpdatePlan: {
386
+ executor: 'v4',
387
+ deviceIdentity: 'pro2-device-id',
388
+ platform: 'native',
389
+ artifacts: [
390
+ { artifactId: 'component:boot', target: 'boot' },
391
+ { artifactId: 'component:app_v1', target: 'app_v1' },
392
+ { artifactId: 'component:se02', target: 'se02' },
393
+ { artifactId: 'component:se03', target: 'se03' },
394
+ ],
395
+ targetsToUpdate: ['boot', 'app_v1', 'se02', 'se03'],
396
+ },
397
+ });
398
+ });
399
+
400
+ test('includes exact Protocol V2 developer force targets in the prepared plan', async () => {
401
+ const currentRelease: IFirmwareReleaseInfo = {
402
+ ...release,
403
+ required: false,
404
+ version: [1, 0, 0],
405
+ installOrder: ['applicationP1', 'coprocessor'],
406
+ components: {
407
+ applicationP1: {
408
+ target: 'APPLICATION_P1',
409
+ url: 'https://example.com/application-p1.okpkg',
410
+ version: [1, 0, 0],
411
+ },
412
+ coprocessor: {
413
+ target: 'COPROCESSOR',
414
+ url: 'https://example.com/coprocessor.okpkg',
415
+ version: [1, 0, 0],
416
+ },
417
+ },
418
+ };
419
+ const method = new CheckAllFirmwareRelease({
420
+ id: 1,
421
+ payload: {
422
+ method: 'checkAllFirmwareRelease',
423
+ platform: 'desktop',
424
+ protocolV2ForceUpdateTargets: ['app_v1', 'coprocessor'],
425
+ },
426
+ });
427
+ method.init();
428
+ method.device = {
429
+ isProtocolV2: () => true,
430
+ features: {
431
+ deviceType: 'pro2',
432
+ serialNo: 'pro2-device-id',
433
+ firmwareVersion: '1.0.0',
434
+ },
435
+ getDeviceState: jest.fn().mockResolvedValue({
436
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
437
+ status: { mode: 'normal' },
438
+ versions: { ...currentVersions, applicationP1: '1.0.0' },
439
+ }),
440
+ } as unknown as CheckAllFirmwareRelease['device'];
441
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(currentRelease);
442
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
443
+
444
+ await expect(method.run()).resolves.toMatchObject({
445
+ targetsToUpdate: ['app_v1', 'coprocessor', 'resource'],
446
+ firmwareUpdatePlan: {
447
+ executor: 'v4',
448
+ platform: 'desktop',
449
+ artifacts: [
450
+ { artifactId: 'component:app_v1', target: 'app_v1' },
451
+ { artifactId: 'component:coprocessor', target: 'coprocessor' },
452
+ ],
453
+ targetsToUpdate: ['app_v1', 'coprocessor'],
454
+ },
455
+ });
456
+ });
457
+
458
+ test('keeps exact force targets but omits a Desktop Plan without device identity', async () => {
459
+ const currentRelease: IFirmwareReleaseInfo = {
460
+ ...release,
461
+ required: false,
462
+ version: [1, 0, 0],
463
+ installOrder: ['applicationP1'],
464
+ components: {
465
+ applicationP1: {
466
+ target: 'APPLICATION_P1',
467
+ url: 'https://example.com/application-p1.okpkg',
468
+ version: [1, 0, 0],
469
+ },
470
+ },
471
+ };
472
+ const method = new CheckAllFirmwareRelease({
473
+ id: 1,
474
+ payload: {
475
+ method: 'checkAllFirmwareRelease',
476
+ platform: 'desktop',
477
+ protocolV2ForceUpdateTargets: ['app_v1'],
478
+ },
479
+ });
480
+ method.init();
481
+ method.device = {
482
+ isProtocolV2: () => true,
483
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
484
+ getDeviceState: jest.fn().mockResolvedValue({
485
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
486
+ status: { mode: 'normal' },
487
+ versions: { ...currentVersions, applicationP1: '1.0.0' },
488
+ }),
489
+ } as unknown as CheckAllFirmwareRelease['device'];
490
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(currentRelease);
491
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
492
+
493
+ await expect(method.run()).resolves.toMatchObject({
494
+ targetsToUpdate: ['app_v1', 'resource'],
495
+ firmwareUpdatePlan: undefined,
496
+ });
497
+ });
498
+
499
+ test('rejects Neo-only unsupported exact secure-element targets', async () => {
500
+ const neoRelease: IFirmwareReleaseInfo = {
501
+ ...release,
502
+ required: false,
503
+ installOrder: ['se03'],
504
+ components: {
505
+ se03: {
506
+ target: 'SE03',
507
+ url: 'https://example.com/se03.okpkg',
508
+ version: [1, 0, 0],
509
+ },
510
+ },
511
+ };
512
+ const method = new CheckAllFirmwareRelease({
513
+ id: 1,
514
+ payload: {
515
+ method: 'checkAllFirmwareRelease',
516
+ protocolV2ForceUpdateTargets: ['se03'],
517
+ },
518
+ });
519
+ method.init();
520
+ method.device = {
521
+ isProtocolV2: () => true,
522
+ features: { deviceType: 'neo', firmwareVersion: '1.0.0' },
523
+ getDeviceState: jest.fn().mockResolvedValue({
524
+ identity: { deviceType: 'neo', firmwareType: EFirmwareType.Universal },
525
+ status: { mode: 'normal' },
526
+ versions: currentVersions,
527
+ }),
528
+ } as unknown as CheckAllFirmwareRelease['device'];
529
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(neoRelease);
530
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
531
+
532
+ await expect(method.run()).rejects.toMatchObject({
533
+ params: { firmwareUpdateCode: 'FirmwarePlanInvalid' },
534
+ });
535
+ });
536
+
537
+ test('builds a V4 plan for Neo and excludes unsupported secure elements when forced', async () => {
538
+ const neoRelease: IFirmwareReleaseInfo = {
539
+ ...release,
540
+ installOrder: ['se01', 'se02', 'se03', 'se04'],
541
+ components: {
542
+ se01: { target: 'SE01', url: 'https://example.com/se01.bin', version: [1, 0, 0] },
543
+ se02: { target: 'SE02', url: 'https://example.com/se02.bin', version: [2, 0, 0] },
544
+ se03: { target: 'SE03', url: 'https://example.com/se03.bin', version: [1, 0, 0] },
545
+ se04: { target: 'SE04', url: 'https://example.com/se04.bin', version: [1, 0, 0] },
546
+ },
547
+ };
548
+ const method = new CheckAllFirmwareRelease({
549
+ id: 1,
550
+ payload: {
551
+ method: 'checkAllFirmwareRelease',
552
+ platform: 'native',
553
+ forceUpdateTargets: ['firmware'],
554
+ },
555
+ });
556
+ method.init();
557
+ method.device = {
558
+ isProtocolV2: () => true,
559
+ features: {
560
+ deviceType: 'neo',
561
+ serialNo: 'neo-device-id',
562
+ firmwareVersion: '1.0.0',
563
+ },
564
+ getDeviceState: jest.fn().mockResolvedValue({
565
+ identity: { deviceType: 'neo', firmwareType: EFirmwareType.Universal },
566
+ status: { mode: 'normal' },
567
+ versions: currentVersions,
568
+ }),
569
+ } as unknown as CheckAllFirmwareRelease['device'];
570
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(neoRelease);
571
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(resourceSource);
572
+
573
+ await expect(method.run()).resolves.toMatchObject({
574
+ targetsToUpdate: ['se01', 'se02', 'resource'],
575
+ firmwareUpdatePlan: {
576
+ executor: 'v4',
577
+ deviceModel: 'neo',
578
+ platform: 'native',
579
+ targetsToUpdate: ['se01', 'se02'],
580
+ },
581
+ });
582
+ });
583
+
584
+ test('rejects a forced Protocol V2 resource target without a configured archive', async () => {
585
+ const method = new CheckAllFirmwareRelease({
586
+ id: 1,
587
+ payload: {
588
+ method: 'checkAllFirmwareRelease',
589
+ forceUpdateTargets: ['resource'],
590
+ },
591
+ });
592
+ method.init();
593
+ method.device = {
594
+ isProtocolV2: () => true,
595
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
596
+ getDeviceState: jest.fn().mockResolvedValue({
597
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
598
+ status: { mode: 'normal' },
599
+ versions: currentVersions,
600
+ }),
601
+ } as unknown as CheckAllFirmwareRelease['device'];
602
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
603
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue(undefined);
604
+
605
+ await expect(method.run()).rejects.toMatchObject({
606
+ params: { firmwareUpdateCode: 'FirmwarePlanInvalid' },
607
+ });
608
+ });
609
+
347
610
  test('requests and compares firmware hashes only when explicitly enabled', async () => {
348
611
  const packageSet: IFirmwareReleaseInfo = {
349
612
  ...release,
@@ -467,6 +730,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
467
730
  resourceStatus: 'unknown',
468
731
  resourcePreparationRequired: true,
469
732
  targetsToUpdate: ['resource'],
733
+ firmwareUpdatePlan: undefined,
470
734
  });
471
735
  });
472
736
 
@@ -477,6 +741,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
477
741
  await api.checkAllFirmwareRelease('pro2-connect-id', {
478
742
  checkFirmwareHash: true,
479
743
  firmwareType: EFirmwareType.Universal,
744
+ protocolV2ForceUpdateTargets: ['app_v1'],
480
745
  retryCount: 0,
481
746
  });
482
747
 
@@ -484,6 +749,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
484
749
  connectId: 'pro2-connect-id',
485
750
  checkFirmwareHash: true,
486
751
  firmwareType: EFirmwareType.Universal,
752
+ protocolV2ForceUpdateTargets: ['app_v1'],
487
753
  retryCount: 0,
488
754
  method: 'checkAllFirmwareRelease',
489
755
  });
@@ -22,6 +22,10 @@ const release: IFirmwareReleaseInfo = {
22
22
  'zh-CN': '更新',
23
23
  'en-US': 'Update',
24
24
  },
25
+ bootloaderChangelog: {
26
+ 'zh-CN': '旧版 Bootloader 更新',
27
+ 'en-US': 'Legacy bootloader update',
28
+ },
25
29
  installOrder: ['bootloader', 'applicationP1', 'coprocessor'],
26
30
  components: {
27
31
  bootloader: {
@@ -127,6 +131,7 @@ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', dev
127
131
  await expect(bootloaderMethod.run()).resolves.toMatchObject({
128
132
  status: 'outdated',
129
133
  shouldUpdate: true,
134
+ changelog: [release.changelog],
130
135
  release: {
131
136
  protocol: 'V2',
132
137
  configKey: 'bootloader',
@@ -134,6 +139,7 @@ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', dev
134
139
  target: 'BOOTLOADER',
135
140
  url: 'https://example.com/bootloader.okpkg',
136
141
  version: [2, 0, 0],
142
+ changelog: release.changelog,
137
143
  },
138
144
  });
139
145
  await expect(allMethod.run()).resolves.toMatchObject({
@@ -162,3 +168,33 @@ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', dev
162
168
  expect(allMethod.getSupportedProtocols()).toEqual(['V1', 'V2']);
163
169
  });
164
170
  });
171
+
172
+ test('returns null from optional release probes when device features are unavailable', async () => {
173
+ const deviceWithoutFeatures = {
174
+ isProtocolV2: jest.fn(() => true),
175
+ features: undefined,
176
+ getDeviceState: jest.fn(),
177
+ };
178
+ const firmwareMethod = new CheckFirmwareRelease({
179
+ id: 1,
180
+ payload: { method: 'checkFirmwareRelease' },
181
+ });
182
+ const bleMethod = new CheckBLEFirmwareRelease({
183
+ id: 2,
184
+ payload: { method: 'checkBLEFirmwareRelease' },
185
+ });
186
+ const bootloaderMethod = new CheckBootloaderRelease({
187
+ id: 3,
188
+ payload: { method: 'checkBootloaderRelease' },
189
+ });
190
+
191
+ firmwareMethod.device = deviceWithoutFeatures as unknown as CheckFirmwareRelease['device'];
192
+ bleMethod.device = deviceWithoutFeatures as unknown as CheckBLEFirmwareRelease['device'];
193
+ bootloaderMethod.device = deviceWithoutFeatures as unknown as CheckBootloaderRelease['device'];
194
+
195
+ await expect(firmwareMethod.run()).resolves.toBeNull();
196
+ await expect(bleMethod.run()).resolves.toBeNull();
197
+ await expect(bootloaderMethod.run()).resolves.toBeNull();
198
+ expect(deviceWithoutFeatures.isProtocolV2).not.toHaveBeenCalled();
199
+ expect(deviceWithoutFeatures.getDeviceState).not.toHaveBeenCalled();
200
+ });
@@ -2778,9 +2778,12 @@ describe('Protocol V2 feature adapter', () => {
2778
2778
  expect(checkedTypes).toEqual(['pro2', 'model_pro2']);
2779
2779
  });
2780
2780
 
2781
- test('uses firmware-v1 as the Pro2 remote firmware config field', () => {
2781
+ test.each([
2782
+ ['Pro2', DeviceType.PRO2],
2783
+ ['Neo', DeviceType.NEO],
2784
+ ] as const)('uses firmware-v1 as the %s remote firmware config field', (_name, deviceType) => {
2782
2785
  const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
2783
- hw: { Device_type: DeviceType.PRO2 },
2786
+ hw: { Device_type: deviceType },
2784
2787
  });
2785
2788
 
2786
2789
  expect(
@@ -5860,6 +5863,80 @@ describe('Protocol V2 firmware update targets', () => {
5860
5863
  getFirmwareLatestReleaseSpy.mockRestore();
5861
5864
  });
5862
5865
 
5866
+ test('downloads only firmware targets that are missing from an explicit mixed payload', async () => {
5867
+ const explicitApplicationBinary = new Uint8Array([1]).buffer;
5868
+ const remoteCoprocessorBinary = new Uint8Array([2]).buffer;
5869
+ const method = new FirmwareUpdateV4({
5870
+ id: 1,
5871
+ payload: {
5872
+ method: 'firmwareUpdateV4',
5873
+ platform: 'web',
5874
+ targetsToUpdate: ['app_v1', 'coprocessor'],
5875
+ },
5876
+ });
5877
+ method.init();
5878
+
5879
+ const getSysResourceBinarySpy = jest
5880
+ .spyOn(firmwareBinaryApi, 'getSysResourceBinary')
5881
+ .mockResolvedValue({ binary: remoteCoprocessorBinary });
5882
+ const getFirmwareLatestReleaseSpy = jest
5883
+ .spyOn(DataManager, 'getFirmwareLatestRelease')
5884
+ .mockReturnValue({
5885
+ required: false,
5886
+ version: [1, 0, 0],
5887
+ url: 'https://example.com/applicationP1.pp.bin',
5888
+ installOrder: ['applicationP1', 'coprocessor'],
5889
+ components: {
5890
+ applicationP1: {
5891
+ target: 'APPLICATION_P1',
5892
+ url: 'https://example.com/applicationP1.pp.bin',
5893
+ },
5894
+ coprocessor: {
5895
+ target: 'COPROCESSOR',
5896
+ url: 'https://example.com/coprocessor.pp.bin',
5897
+ },
5898
+ },
5899
+ fingerprint: '',
5900
+ changelog: {
5901
+ 'zh-CN': '',
5902
+ 'en-US': '',
5903
+ },
5904
+ });
5905
+
5906
+ const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries(
5907
+ 'universal',
5908
+ { deviceType: 'pro2', firmwareVersion: '0.0.0' },
5909
+ [
5910
+ {
5911
+ fileName: 'application_p1.bin',
5912
+ binary: explicitApplicationBinary,
5913
+ targetId: 4,
5914
+ kind: 'firmware',
5915
+ },
5916
+ ]
5917
+ );
5918
+
5919
+ expect(getSysResourceBinarySpy).toHaveBeenCalledTimes(1);
5920
+ expect(getSysResourceBinarySpy).toHaveBeenCalledWith('https://example.com/coprocessor.pp.bin');
5921
+ expect(remoteBinaries.installItems).toEqual([
5922
+ {
5923
+ fileName: 'application_p1.bin',
5924
+ binary: explicitApplicationBinary,
5925
+ targetId: 4,
5926
+ kind: 'firmware',
5927
+ },
5928
+ {
5929
+ fileName: 'coprocessor.bin',
5930
+ binary: remoteCoprocessorBinary,
5931
+ targetId: 6,
5932
+ kind: 'firmware',
5933
+ },
5934
+ ]);
5935
+
5936
+ getSysResourceBinarySpy.mockRestore();
5937
+ getFirmwareLatestReleaseSpy.mockRestore();
5938
+ });
5939
+
5863
5940
  test('ignores an unsupported remote ROMLOADER component when installing selected targets', async () => {
5864
5941
  const method = new FirmwareUpdateV4({
5865
5942
  id: 1,
@@ -6195,6 +6272,108 @@ describe('Protocol V2 firmware update targets', () => {
6195
6272
  );
6196
6273
  });
6197
6274
 
6275
+ test('downloads every missing firmware target when manifest resources are already prepared', async () => {
6276
+ const resourceBundle = createProtocolV2OkppBinary();
6277
+ const applicationBinary = new Uint8Array([7]).buffer;
6278
+ const remoteBinaries = {
6279
+ bootloaderBinary: null,
6280
+ fwBinaryMap: [
6281
+ {
6282
+ fileName: 'application_p1.bin',
6283
+ binary: applicationBinary,
6284
+ targetId: 4,
6285
+ },
6286
+ ],
6287
+ installItems: [
6288
+ {
6289
+ fileName: 'application_p1.bin',
6290
+ binary: applicationBinary,
6291
+ targetId: 4,
6292
+ kind: 'firmware',
6293
+ },
6294
+ ],
6295
+ };
6296
+ const method = new FirmwareUpdateV4({
6297
+ id: 1,
6298
+ payload: {
6299
+ method: 'firmwareUpdateV4',
6300
+ platform: 'web',
6301
+ targetsToUpdate: ['app_v1', 'resource'],
6302
+ resourceFiles: [
6303
+ {
6304
+ binary: resourceBundle,
6305
+ devicePath: 'vol0:/bundles/images/images.okpkg',
6306
+ },
6307
+ ],
6308
+ },
6309
+ });
6310
+ method.init();
6311
+ (method as any).captureProtocolV2PhysicalIdentity = jest.fn().mockResolvedValue(undefined);
6312
+ (method as any).device = stubDevice({
6313
+ originalDescriptor: { protocolType: 'V2' },
6314
+ features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
6315
+ });
6316
+ (method as any).prepareRemoteProtocolV2Binaries = jest.fn().mockResolvedValue(remoteBinaries);
6317
+ (method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
6318
+ method.postTipMessage = jest.fn();
6319
+
6320
+ await method.run();
6321
+
6322
+ expect(forceReloadDataSpy).toHaveBeenCalledTimes(1);
6323
+ expect((method as any).prepareRemoteProtocolV2Binaries).toHaveBeenCalledWith(
6324
+ 'universal',
6325
+ expect.objectContaining({ deviceType: 'pro2' }),
6326
+ []
6327
+ );
6328
+ expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
6329
+ bootloaderBinary: null,
6330
+ fwBinaryMap: remoteBinaries.fwBinaryMap,
6331
+ installItems: remoteBinaries.installItems,
6332
+ resourceBundles: [
6333
+ {
6334
+ name: 'images.okpkg',
6335
+ binary: resourceBundle,
6336
+ devicePath: 'vol0:/bundles/images/images.okpkg',
6337
+ version: [1, 2, 3],
6338
+ payloadHash: '11'.repeat(64),
6339
+ headerHash: '22'.repeat(64),
6340
+ },
6341
+ ],
6342
+ });
6343
+ });
6344
+
6345
+ test('rejects a mixed resource plan when the requested firmware release is unavailable', async () => {
6346
+ const resourceBundle = createProtocolV2OkppBinary();
6347
+ const method = new FirmwareUpdateV4({
6348
+ id: 1,
6349
+ payload: {
6350
+ method: 'firmwareUpdateV4',
6351
+ platform: 'web',
6352
+ targetsToUpdate: ['app_v1', 'resource'],
6353
+ resourceFiles: [
6354
+ {
6355
+ binary: resourceBundle,
6356
+ devicePath: 'vol0:/bundles/images/images.okpkg',
6357
+ },
6358
+ ],
6359
+ },
6360
+ });
6361
+ method.init();
6362
+ (method as any).captureProtocolV2PhysicalIdentity = jest.fn().mockResolvedValue(undefined);
6363
+ (method as any).device = stubDevice({
6364
+ originalDescriptor: { protocolType: 'V2' },
6365
+ features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
6366
+ });
6367
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(undefined);
6368
+ (method as any).executeProtocolV2Update = jest.fn();
6369
+ method.postTipMessage = jest.fn();
6370
+
6371
+ await expect(method.run()).rejects.toThrow(
6372
+ 'Protocol V2 firmware release is unavailable for requested targets: app_v1'
6373
+ );
6374
+ expect((method as any).executeProtocolV2Update).not.toHaveBeenCalled();
6375
+ });
6376
+
6198
6377
  test('combines prepared firmware components with explicit manifest resource files', async () => {
6199
6378
  const resourceBundle = createProtocolV2OkppBinary();
6200
6379
  const method = new FirmwareUpdateV4({
@@ -1 +1 @@
1
- {"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA8B1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EACV,kBAAkB,EAElB,kCAAkC,EAClC,+BAA+B,EAChC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,UAAU,CAAC;AAgIlB,MAAM,MAAM,6BAA6B,GAAG;IAC1C,YAAY,EAAE,aAAa,CAAC;IAC5B,MAAM,EAAE,+BAA+B,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,kCAAkC,EAAE,CAAC;IACjD,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,EAC7C,eAAe,EACf,mBAAwB,EACxB,iBAAyB,EACzB,mBAAwB,EACxB,YAAY,EACZ,OAAO,EACP,UAAU,GACX,EAAE;IACD,eAAe,EAAE,mBAAmB,CAAC;IACrC,mBAAmB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,YAAY,EAAE,aAAa,CAAC;IAC5B,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC7B,GAAG,6BAA6B,CAiFhC;AAID,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAME,GAAG;YAuFK,aAAa;CA+D5B"}
1
+ {"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EACV,kBAAkB,EAElB,kCAAkC,EAClC,+BAA+B,EAChC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,UAAU,CAAC;AAgIlB,MAAM,MAAM,6BAA6B,GAAG;IAC1C,YAAY,EAAE,aAAa,CAAC;IAC5B,MAAM,EAAE,+BAA+B,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,kCAAkC,EAAE,CAAC;IACjD,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,EAC7C,eAAe,EACf,mBAAwB,EACxB,iBAAyB,EACzB,mBAAwB,EACxB,YAAY,EACZ,OAAO,EACP,UAAU,GACX,EAAE;IACD,eAAe,EAAE,mBAAmB,CAAC;IACrC,mBAAmB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,YAAY,EAAE,aAAa,CAAC;IAC5B,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC7B,GAAG,6BAA6B,CAiFhC;AAID,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAME,GAAG;YAuFK,aAAa;CAmI5B"}
@@ -10,6 +10,6 @@ export default class CheckBLEFirmwareRelease extends BaseMethod {
10
10
  }[];
11
11
  release: import("..").IBLEFirmwareReleaseInfo | undefined;
12
12
  bootloaderMode: boolean;
13
- }>;
13
+ } | null>;
14
14
  }
15
15
  //# sourceMappingURL=CheckBLEFirmwareRelease.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CheckBLEFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckBLEFirmwareRelease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAY1C,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAUE,GAAG;;;;;;;;;CA+BV"}
1
+ {"version":3,"file":"CheckBLEFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckBLEFirmwareRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAY1C,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAUE,GAAG;;;;;;;;;CA2BV"}
@@ -11,6 +11,6 @@ export default class CheckBootloaderRelease extends BaseMethod {
11
11
  release: import("..").IFirmwareReleaseInfo | undefined;
12
12
  bootloaderMode: boolean;
13
13
  shouldUpdate: boolean;
14
- }>;
14
+ } | null>;
15
15
  }
16
16
  //# sourceMappingURL=CheckBootloaderRelease.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CheckBootloaderRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckBootloaderRelease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAc1C,MAAM,CAAC,OAAO,OAAO,sBAAuB,SAAQ,UAAU;IAC5D,qBAAqB;IAIrB,IAAI;IAME,GAAG;;;;;;;;;;CA0CV"}
1
+ {"version":3,"file":"CheckBootloaderRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckBootloaderRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAc1C,MAAM,CAAC,OAAO,OAAO,sBAAuB,SAAQ,UAAU;IAC5D,qBAAqB;IAIrB,IAAI;IAME,GAAG;;;;;;;;;;CAqCV"}
@@ -10,6 +10,6 @@ export default class CheckFirmwareRelease extends BaseMethod {
10
10
  }[];
11
11
  release: import("..").IFirmwareReleaseInfo | undefined;
12
12
  bootloaderMode: boolean;
13
- }>;
13
+ } | null>;
14
14
  }
15
15
  //# sourceMappingURL=CheckFirmwareRelease.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CheckFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckFirmwareRelease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAa1C,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,UAAU;IAC1D,qBAAqB;IAIrB,IAAI;IAUE,GAAG;;;;;;;;;CAiCV"}
1
+ {"version":3,"file":"CheckFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckFirmwareRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAa1C,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,UAAU;IAC1D,qBAAqB;IAIrB,IAAI;IAUE,GAAG;;;;;;;;;CA6BV"}
@@ -40,6 +40,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
40
40
  private verifyProtocolV2ReconnectIdentity;
41
41
  private prepareBootloaderBinary;
42
42
  private hasExplicitProtocolV2Payload;
43
+ private getMissingProtocolV2FirmwareTargets;
43
44
  private buildProtocolV2InstallItems;
44
45
  private getRemoteComponentEntries;
45
46
  private getRemoteComponentTarget;
@@ -1 +1 @@
1
- {"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkD,MAAM,qBAAqB,CAAC;AAyBlG,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAmB/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AA6CrC,wBAAgB,wCAAwC,CACtD,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,EAC5C,MAAM,EAAE,sBAAsB,QAgB/B;AA6UD,eAAO,MAAM,oCAAoC,WACvC,WAAW,GAAG,UAAU,eACnB,MAAM,GAAG,SAAS,YAKhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,iBACZ,MAAM,eACR,MAAM,SAsBpB,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,OAAO,CAAC,sBAAsB,CAAC,CAAS;IAExC,qBAAqB;IAIrB,OAAO,CAAC,yBAAyB,CAA4B;IAE7D,OAAO,CAAC,2BAA2B,CAAS;IAE5C,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,6BAA6B,CAAC,CAAW;IAEjD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,IAAI;IAoHJ,OAAO,CAAC,8BAA8B;IAwBhC,GAAG;;;;;YAKK,aAAa;YAwGb,4BAA4B;YAkB5B,0BAA0B;YAY1B,8BAA8B;YAK9B,+BAA+B;YAqD/B,gCAAgC;YAmDhC,8BAA8B;IAkC5C,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,kCAAkC;IAc1C,OAAO,CAAC,gCAAgC;IAsDxC,OAAO,CAAC,6BAA6B;YAsBvB,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;IAS1C,OAAO,CAAC,8BAA8B;YAMxB,iCAAiC;YAOjC,iCAAiC;YAkBjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YAmCjC,+BAA+B;IAqD7C,OAAO,CAAC,sCAAsC;IA0C9C,OAAO,CAAC,6BAA6B;YAMvB,8BAA8B;YA8C9B,kCAAkC;IA4BhD,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,yBAAyB;IAc3B,6BAA6B;YAgCrB,+BAA+B;IAkD7C,OAAO,CAAC,6BAA6B;IAkCrC,OAAO,CAAC,8BAA8B;YA8CxB,uBAAuB;YAiCvB,6BAA6B;YAa7B,uBAAuB;YA6CvB,8BAA8B;YA8D9B,6BAA6B;IAuE3C,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,4BAA4B;IAyGpC,OAAO,CAAC,6BAA6B;YAcvB,uCAAuC;YA0JvC,gCAAgC;YAehC,yBAAyB;YAQzB,8BAA8B;IAQ5C,OAAO,CAAC,0BAA0B;YAkBpB,mCAAmC;YAWnC,qCAAqC;YAgDrC,yBAAyB;YA8DzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAQ5B,cAAc;YAuCd,6BAA6B;YAW7B,0BAA0B;YAS1B,6BAA6B;YAmB7B,gBAAgB;IAiB9B,OAAO,CAAC,qBAAqB;CAM9B"}
1
+ {"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkD,MAAM,qBAAqB,CAAC;AAyBlG,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAmB/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AA6CrC,wBAAgB,wCAAwC,CACtD,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,EAC5C,MAAM,EAAE,sBAAsB,QAgB/B;AA6UD,eAAO,MAAM,oCAAoC,WACvC,WAAW,GAAG,UAAU,eACnB,MAAM,GAAG,SAAS,YAKhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,iBACZ,MAAM,eACR,MAAM,SAsBpB,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,OAAO,CAAC,sBAAsB,CAAC,CAAS;IAExC,qBAAqB;IAIrB,OAAO,CAAC,yBAAyB,CAA4B;IAE7D,OAAO,CAAC,2BAA2B,CAAS;IAE5C,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,6BAA6B,CAAC,CAAW;IAEjD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,IAAI;IAoHJ,OAAO,CAAC,8BAA8B;IAwBhC,GAAG;;;;;YAKK,aAAa;YAgHb,4BAA4B;YAkB5B,0BAA0B;YAY1B,8BAA8B;YAK9B,+BAA+B;YAqD/B,gCAAgC;YAmDhC,8BAA8B;IAkC5C,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,kCAAkC;IAc1C,OAAO,CAAC,gCAAgC;IAsDxC,OAAO,CAAC,6BAA6B;YAsBvB,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;IAS1C,OAAO,CAAC,8BAA8B;YAMxB,iCAAiC;YAOjC,iCAAiC;YAkBjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IAIpC,OAAO,CAAC,mCAAmC;IAY3C,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YAmCjC,+BAA+B;IA6E7C,OAAO,CAAC,sCAAsC;IA0C9C,OAAO,CAAC,6BAA6B;YAMvB,8BAA8B;YA8C9B,kCAAkC;IA4BhD,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,yBAAyB;IAc3B,6BAA6B;YAgCrB,+BAA+B;IAkD7C,OAAO,CAAC,6BAA6B;IAkCrC,OAAO,CAAC,8BAA8B;YA8CxB,uBAAuB;YAiCvB,6BAA6B;YAa7B,uBAAuB;YA6CvB,8BAA8B;YA8D9B,6BAA6B;IAuE3C,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,4BAA4B;IAyGpC,OAAO,CAAC,6BAA6B;YAcvB,uCAAuC;YA0JvC,gCAAgC;YAehC,yBAAyB;YAQzB,8BAA8B;IAQ5C,OAAO,CAAC,0BAA0B;YAkBpB,mCAAmC;YAWnC,qCAAqC;YAgDrC,yBAAyB;YA8DzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAQ5B,cAAc;YAuCd,6BAA6B;YAW7B,0BAA0B;YAS1B,6BAA6B;YAmB7B,gBAAgB;IAiB9B,OAAO,CAAC,qBAAqB;CAM9B"}