@onekeyfe/hd-core 1.2.0-alpha.97 → 1.2.0-alpha.99

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 (103) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +411 -86
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  3. package/__tests__/firmware-memory-host.test.ts +126 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -2
  5. package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
  6. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +573 -0
  7. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
  8. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +65 -2
  9. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
  10. package/__tests__/firmware-update/firmware-update-plan.test.ts +293 -71
  11. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  12. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  13. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  14. package/__tests__/protocol-v2-firmware-targets.test.ts +16 -0
  15. package/__tests__/protocol-v2-resources.test.ts +148 -232
  16. package/__tests__/protocol-v2.test.ts +1326 -510
  17. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
  18. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  19. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  20. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  21. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  22. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  23. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  24. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  25. package/dist/api/FirmwareUpdateV2.d.ts +1 -0
  26. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  27. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  28. package/dist/api/FirmwareUpdateV4.d.ts +11 -9
  29. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  30. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  31. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  32. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  33. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  34. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  35. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  36. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  37. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  38. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  39. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
  40. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  41. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +7 -1
  42. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  43. package/dist/api/firmware/getBinary.d.ts +0 -1
  44. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  45. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  46. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  47. package/dist/data-manager/DataManager.d.ts +4 -3
  48. package/dist/data-manager/DataManager.d.ts.map +1 -1
  49. package/dist/index.d.ts +108 -114
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1825 -1006
  52. package/dist/protocols/protocol-v2/resources.d.ts +10 -28
  53. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  54. package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
  55. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  56. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  57. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  58. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  59. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  60. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  61. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  62. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  63. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  64. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  65. package/dist/types/api/export.d.ts +1 -1
  66. package/dist/types/api/export.d.ts.map +1 -1
  67. package/dist/types/api/firmwareUpdate.d.ts +4 -12
  68. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  69. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  70. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  71. package/dist/types/settings.d.ts +35 -36
  72. package/dist/types/settings.d.ts.map +1 -1
  73. package/package.json +4 -4
  74. package/src/api/CheckAllFirmwareRelease.ts +113 -80
  75. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  76. package/src/api/CheckBootloaderRelease.ts +35 -3
  77. package/src/api/CheckFirmwareRelease.ts +35 -7
  78. package/src/api/FirmwareUpdateV2.ts +231 -138
  79. package/src/api/FirmwareUpdateV3.ts +101 -56
  80. package/src/api/FirmwareUpdateV4.ts +936 -408
  81. package/src/api/UploadPortfolio.ts +18 -0
  82. package/src/api/device/DeviceUpdateBootloader.ts +37 -11
  83. package/src/api/firmware/FirmwareHostBinding.ts +26 -3
  84. package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
  85. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  86. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +122 -73
  87. package/src/api/firmware/FirmwareUpdatePlan.ts +295 -122
  88. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +49 -12
  89. package/src/api/firmware/protocolV2Release.ts +168 -0
  90. package/src/data-manager/DataManager.ts +35 -19
  91. package/src/index.ts +10 -0
  92. package/src/protocols/protocol-v2/resources.ts +157 -341
  93. package/src/types/api/checkAllFirmwareRelease.ts +27 -12
  94. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  95. package/src/types/api/checkBootloaderRelease.ts +2 -6
  96. package/src/types/api/checkFirmwareRelease.ts +2 -13
  97. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  98. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  99. package/src/types/api/export.ts +6 -1
  100. package/src/types/api/firmwareUpdate.ts +12 -17
  101. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  102. package/src/types/settings.ts +35 -61
  103. package/src/utils/deviceFeaturesUtils.ts +2 -2
package/dist/index.js CHANGED
@@ -522,6 +522,10 @@ const PROTOCOL_V2_TARGETS = {
522
522
  SE03: 'se03',
523
523
  SE04: 'se04',
524
524
  };
525
+ const PROTOCOL_V2_FIRMWARE_UPDATE_TARGETS = new Set([
526
+ ...Object.values(PROTOCOL_V2_TARGETS),
527
+ 'resource',
528
+ ]);
525
529
  const asRecord = (value) => value && typeof value === 'object' && !Array.isArray(value)
526
530
  ? value
527
531
  : undefined;
@@ -591,6 +595,16 @@ const planError = (message) => {
591
595
  firmwareUpdateCode: 'FirmwarePlanInvalid',
592
596
  });
593
597
  };
598
+ const requireArtifactIntegrity = (input, label) => {
599
+ const integrity = asIntegrity(input);
600
+ if (integrity.expectedSize === undefined || integrity.expectedSha256 === undefined) {
601
+ return planError(`${label} integrity metadata is invalid`);
602
+ }
603
+ return {
604
+ expectedSize: integrity.expectedSize,
605
+ expectedSha256: integrity.expectedSha256,
606
+ };
607
+ };
594
608
  const FIRMWARE_UPDATE_PLAN_FORCE_TARGETS = new Set([
595
609
  'firmware',
596
610
  'ble',
@@ -609,6 +623,21 @@ const validateFirmwareUpdatePlanForceTargets = (value) => {
609
623
  }
610
624
  return [...value];
611
625
  };
626
+ const validateProtocolV2FirmwareUpdateTargets = (value) => {
627
+ if (value === undefined) {
628
+ return [];
629
+ }
630
+ if (!Array.isArray(value)) {
631
+ return planError('Protocol V2 firmware update targets are invalid');
632
+ }
633
+ const normalizedTargets = value.map(target => target === 'boot_resources' ? 'resource' : target);
634
+ if (normalizedTargets.length > PROTOCOL_V2_FIRMWARE_UPDATE_TARGETS.size ||
635
+ normalizedTargets.some(target => !PROTOCOL_V2_FIRMWARE_UPDATE_TARGETS.has(target)) ||
636
+ new Set(normalizedTargets).size !== normalizedTargets.length) {
637
+ return planError('Protocol V2 firmware update targets are invalid');
638
+ }
639
+ return normalizedTargets;
640
+ };
612
641
  const assertExactKeys = (value, required, optional = []) => {
613
642
  const allowed = new Set([...required, ...optional]);
614
643
  if (required.some(key => !Object.prototype.hasOwnProperty.call(value, key)) ||
@@ -689,7 +718,7 @@ const assertFirmwareUpdatePlan = (value) => {
689
718
  if (!artifact) {
690
719
  return planError('Firmware update plan artifact is invalid');
691
720
  }
692
- assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container'], ['logicalName', 'expectedSize', 'expectedSha256', 'targetVersion']);
721
+ assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container', 'expectedSize', 'expectedSha256'], ['logicalName', 'targetVersion']);
693
722
  const artifactId = assertBoundedString(artifact.artifactId, 'artifact id', 160);
694
723
  if (artifactIds.has(artifactId)) {
695
724
  return planError('Firmware update plan contains duplicate artifact ids');
@@ -705,13 +734,11 @@ const assertFirmwareUpdatePlan = (value) => {
705
734
  if (artifact.logicalName !== undefined) {
706
735
  assertBoundedString(artifact.logicalName, 'logical name', 256);
707
736
  }
708
- if (artifact.expectedSize !== undefined &&
709
- (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0)) {
737
+ if (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0) {
710
738
  return planError('Firmware update plan artifact size is invalid');
711
739
  }
712
- if (artifact.expectedSha256 !== undefined &&
713
- (typeof artifact.expectedSha256 !== 'string' ||
714
- !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256))) {
740
+ if (typeof artifact.expectedSha256 !== 'string' ||
741
+ !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256)) {
715
742
  return planError('Firmware update plan artifact digest is invalid');
716
743
  }
717
744
  if (artifact.targetVersion !== undefined) {
@@ -738,7 +765,7 @@ const assertFirmwareUpdatePlan = (value) => {
738
765
  plan.targetsToUpdate.some(target => legacyOnlyTargets.has(target))) ||
739
766
  (plan.artifacts.length > 0 &&
740
767
  (plan.platform === 'native' || plan.platform === 'desktop') &&
741
- plan.executor !== 'v2' &&
768
+ plan.executor === 'v3' &&
742
769
  plan.deviceIdentity === 'unavailable')) {
743
770
  return planError('Firmware update plan executor contract is invalid');
744
771
  }
@@ -773,7 +800,7 @@ const selectResourceUrl = ({ release, features, platform, }) => {
773
800
  };
774
801
  const getExecutor = (features) => {
775
802
  const deviceType = getDeviceType(features);
776
- if (deviceType === hdShared.EDeviceType.Pro2) {
803
+ if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo) {
777
804
  return 'v4';
778
805
  }
779
806
  if (deviceType === hdShared.EDeviceType.Pro &&
@@ -782,7 +809,7 @@ const getExecutor = (features) => {
782
809
  }
783
810
  return 'v2';
784
811
  };
785
- const buildProtocolV2Artifacts = (release, { includeComponents = true, includeResources = true, } = {}) => {
812
+ const buildProtocolV2Artifacts = (release, { includeComponents = true, componentTargets: selectedComponentTargets, } = {}) => {
786
813
  var _a;
787
814
  const components = asRecord(release.components);
788
815
  if (includeComponents && !components) {
@@ -794,49 +821,36 @@ const buildProtocolV2Artifacts = (release, { includeComponents = true, includeRe
794
821
  const componentKeys = includeComponents && components
795
822
  ? [...installOrder, ...Object.keys(components).filter(key => !installOrder.includes(key))]
796
823
  : [];
824
+ const selectedComponentKeys = selectedComponentTargets
825
+ ? componentKeys.filter(key => {
826
+ var _a;
827
+ const component = asRecord(components === null || components === void 0 ? void 0 : components[key]);
828
+ const targetName = (_a = asString(component === null || component === void 0 ? void 0 : component.target)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
829
+ const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
830
+ return !!target && selectedComponentTargets.has(target);
831
+ })
832
+ : componentKeys;
797
833
  const artifacts = [];
798
834
  const targets = [];
799
- const componentTargets = new Set();
800
- for (const key of componentKeys) {
835
+ const componentTargetSet = new Set();
836
+ for (const key of selectedComponentKeys) {
801
837
  const component = asRecord(components === null || components === void 0 ? void 0 : components[key]);
802
838
  const targetName = (_a = asString(component === null || component === void 0 ? void 0 : component.target)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
839
+ const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
803
840
  if (targetName === 'ROMLOADER') {
804
841
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 ROMLOADER requires its dedicated loader flow', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
805
842
  }
806
- const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
807
843
  if (!component || !target) {
808
844
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 component ${key} has an unsupported target`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
809
845
  }
810
- if (componentTargets.has(target)) {
846
+ if (componentTargetSet.has(target)) {
811
847
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 component ${key} duplicates target ${target}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
812
848
  }
813
- componentTargets.add(target);
814
- artifacts.push(Object.assign(Object.assign({ artifactId: `component:${target}`, role: 'component', target, url: assertArtifactUrl(component.url, `Protocol V2 component ${key}`), container: 'raw', logicalName: key }, asIntegrity({
815
- size: component.expectedSize,
816
- sha256: component.fingerprint,
817
- })), (asVersion(component.version) ? { targetVersion: asVersion(component.version) } : {})));
849
+ componentTargetSet.add(target);
850
+ const integrity = requireArtifactIntegrity({ size: component.expectedSize, sha256: component.fingerprint }, `Protocol V2 component ${key}`);
851
+ artifacts.push(Object.assign(Object.assign({ artifactId: `component:${target}`, role: 'component', target, url: assertArtifactUrl(component.url, `Protocol V2 component ${key}`), container: 'raw', logicalName: key }, integrity), (asVersion(component.version) ? { targetVersion: asVersion(component.version) } : {})));
818
852
  targets.push(target);
819
853
  }
820
- const bundles = includeResources && Array.isArray(release.resourceBundles) ? release.resourceBundles : [];
821
- const resourceBundleNames = new Set();
822
- for (const value of bundles) {
823
- const bundle = asRecord(value);
824
- const name = asString(bundle === null || bundle === void 0 ? void 0 : bundle.name);
825
- if (!bundle || !name) {
826
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource bundle is invalid', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
827
- }
828
- if (resourceBundleNames.has(name)) {
829
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 resource bundle duplicates name ${name}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
830
- }
831
- resourceBundleNames.add(name);
832
- artifacts.push(Object.assign({ artifactId: `resourceBundle:${name}`, role: 'resourceBundle', target: 'resource', url: assertArtifactUrl(bundle.url, `Protocol V2 resource bundle ${name}`), container: 'raw', logicalName: name }, asIntegrity({
833
- size: bundle.expectedSize,
834
- sha256: bundle.fingerprint,
835
- })));
836
- }
837
- if (bundles.length > 0) {
838
- targets.push('resource');
839
- }
840
854
  return { artifacts, targets: [...new Set(targets)] };
841
855
  };
842
856
  const isForcedTargetRepresented = ({ executor, forcedTarget, artifacts, targetsToUpdate, }) => {
@@ -865,8 +879,91 @@ const assertForcedTargetsRepresented = ({ executor, forcedTargets, artifacts, ta
865
879
  }
866
880
  }
867
881
  };
882
+ const finalizeFirmwareUpdatePlan = ({ features, firmwareType, platform, artifacts, targetsToUpdate, }) => {
883
+ const executor = getExecutor(features);
884
+ const bootloaderMode = resolveDeviceBootloaderMode(features);
885
+ const reportedIdentity = getDeviceUUID(features);
886
+ const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
887
+ const allowsUnavailableIdentity = (bootloaderMode && executor === 'v2') || executor === 'v4';
888
+ if (artifacts.length > 0 &&
889
+ (platform === 'native' || platform === 'desktop') &&
890
+ !deviceIdentity &&
891
+ !allowsUnavailableIdentity) {
892
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware update requires a stable device identity', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
893
+ }
894
+ const planWithoutDigest = {
895
+ schemaVersion: 2,
896
+ executor,
897
+ deviceIdentity: deviceIdentity || 'unavailable',
898
+ deviceModel: String(getDeviceType(features)),
899
+ firmwareType,
900
+ platform,
901
+ artifacts,
902
+ targetsToUpdate,
903
+ };
904
+ return assertFirmwareUpdatePlan(Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) }));
905
+ };
906
+ const buildProtocolV2LocalFirmwareUpdatePlan = ({ features, firmwareType, platform, artifacts, }) => {
907
+ if (artifacts.length === 0) {
908
+ return planError('Protocol V2 local firmware plan has no artifacts');
909
+ }
910
+ const plan = finalizeFirmwareUpdatePlan({
911
+ features,
912
+ firmwareType,
913
+ platform,
914
+ artifacts: artifacts.map(artifact => (Object.assign(Object.assign({}, artifact), { role: artifact.target === 'resource' ? 'resourceBundle' : 'component', url: `https://local-firmware.invalid/${encodeURIComponent(artifact.artifactId)}` }))),
915
+ targetsToUpdate: artifacts.map(artifact => artifact.target),
916
+ });
917
+ if (plan.executor !== 'v4') {
918
+ return planError('Protocol V2 local firmware plan requires executor v4');
919
+ }
920
+ return plan;
921
+ };
922
+ const buildProtocolV2FirmwareUpdatePlan = ({ features, firmwareType, platform, release, targetsToUpdate, forceUpdateTargets, resourceArchive, }) => {
923
+ const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
924
+ if (validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
925
+ planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
926
+ }
927
+ const requestedComponentTargets = new Set(targetsToUpdate.filter(target => target !== 'resource'));
928
+ if (getDeviceType(features) === hdShared.EDeviceType.Neo &&
929
+ (requestedComponentTargets.has('se03') || requestedComponentTargets.has('se04'))) {
930
+ planError('Neo does not support Protocol V2 targets se03 or se04');
931
+ }
932
+ const protocolV2 = buildProtocolV2Artifacts(release !== null && release !== void 0 ? release : {}, {
933
+ includeComponents: requestedComponentTargets.size > 0,
934
+ componentTargets: requestedComponentTargets,
935
+ });
936
+ const resourceRequested = targetsToUpdate.includes('resource');
937
+ if (resourceRequested) {
938
+ const archive = resourceArchive;
939
+ if (!archive) {
940
+ return planError('Protocol V2 resource target has no resource archive');
941
+ }
942
+ const integrity = requireArtifactIntegrity({ size: archive.archiveSize, sha256: archive.archiveSha256 }, 'Protocol V2 resource archive');
943
+ protocolV2.artifacts.push(Object.assign({ artifactId: 'resource:archive', role: 'resourceBundle', target: 'resource', url: assertArtifactUrl(archive.archiveUrl, 'Protocol V2 resource archive'), container: 'zip', logicalName: 'protocol-v2-resource-archive' }, integrity));
944
+ protocolV2.targets.push('resource');
945
+ }
946
+ const representedTargets = new Set(protocolV2.targets);
947
+ const missingTarget = Array.from(requestedComponentTargets).find(target => !representedTargets.has(target));
948
+ if (missingTarget) {
949
+ planError(`Protocol V2 release does not contain requested target ${missingTarget}`);
950
+ }
951
+ if (validatedForceTargets.includes('firmware') && protocolV2.targets.length === 0) {
952
+ planError('Forced firmware update target firmware is not represented by the plan');
953
+ }
954
+ if (validatedForceTargets.includes('resource') && !resourceArchive) {
955
+ planError('Forced firmware update target resource has no Protocol V2 resource archive');
956
+ }
957
+ return finalizeFirmwareUpdatePlan({
958
+ features,
959
+ firmwareType,
960
+ platform,
961
+ artifacts: protocolV2.artifacts,
962
+ targetsToUpdate: protocolV2.targets,
963
+ });
964
+ };
868
965
  const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, ble, bootloader, forceUpdateTargets, }) => {
869
- var _a, _b, _c, _d, _e, _f, _g;
966
+ var _a, _b;
870
967
  const executor = getExecutor(features);
871
968
  let artifacts = [];
872
969
  let targetsToUpdate = [];
@@ -878,7 +975,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
878
975
  ((release === null || release === void 0 ? void 0 : release.status) === 'none' || (release === null || release === void 0 ? void 0 : release.status) === 'unknown') &&
879
976
  !!asRelease(release);
880
977
  const shouldUpdateFirmware = isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
881
- const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
978
+ const shouldUpdateResource = !(executor === 'v2' && bootloaderMode) &&
979
+ (isUpgrade(firmware) || forcedTargets.has('resource'));
882
980
  if (executor === 'v4' &&
883
981
  validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
884
982
  planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
@@ -886,19 +984,20 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
886
984
  if (executor === 'v4' && (shouldUpdateFirmware || shouldUpdateResource)) {
887
985
  const protocolV2 = buildProtocolV2Artifacts(firmwareRelease !== null && firmwareRelease !== void 0 ? firmwareRelease : {}, {
888
986
  includeComponents: shouldUpdateFirmware,
889
- includeResources: shouldUpdateResource,
890
987
  });
891
988
  artifacts = protocolV2.artifacts;
892
989
  targetsToUpdate = protocolV2.targets;
893
990
  }
894
991
  else {
895
992
  if (isUpgrade(bootloader) || forcedTargets.has('bootloader')) {
896
- artifacts.push(Object.assign(Object.assign({ artifactId: 'bootloader', role: 'bootloader', target: 'bootloader', url: assertArtifactUrl((_a = asRelease(bootloader)) === null || _a === void 0 ? void 0 : _a.bootloaderResource, 'Bootloader release'), container: 'raw' }, asIntegrity({
897
- size: (_b = asRelease(bootloader)) === null || _b === void 0 ? void 0 : _b.bootloaderExpectedSize,
898
- sha256: (_c = asRelease(bootloader)) === null || _c === void 0 ? void 0 : _c.bootloaderFingerprint,
899
- })), (asVersion((_d = asRelease(bootloader)) === null || _d === void 0 ? void 0 : _d.bootloaderVersion)
993
+ const bootloaderRelease = asRelease(bootloader);
994
+ const integrity = requireArtifactIntegrity({
995
+ size: bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderExpectedSize,
996
+ sha256: bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderFingerprint,
997
+ }, 'Bootloader release');
998
+ artifacts.push(Object.assign(Object.assign({ artifactId: 'bootloader', role: 'bootloader', target: 'bootloader', url: assertArtifactUrl(bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderResource, 'Bootloader release'), container: 'raw' }, integrity), (asVersion(bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderVersion)
900
999
  ? {
901
- targetVersion: asVersion((_e = asRelease(bootloader)) === null || _e === void 0 ? void 0 : _e.bootloaderVersion),
1000
+ targetVersion: asVersion(bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderVersion),
902
1001
  }
903
1002
  : {})));
904
1003
  }
@@ -909,10 +1008,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
909
1008
  });
910
1009
  }
911
1010
  if (shouldUpdateFirmware) {
912
- artifacts.push(Object.assign(Object.assign({ artifactId: 'firmware', role: 'firmware', target: 'firmware', url: assertArtifactUrl(firmwareRelease.url, 'Firmware release'), container: 'raw' }, asIntegrity({
913
- size: firmwareRelease.expectedSize,
914
- sha256: firmwareRelease.fingerprint,
915
- })), (asVersion(firmwareRelease.version)
1011
+ const integrity = requireArtifactIntegrity({ size: firmwareRelease.expectedSize, sha256: firmwareRelease.fingerprint }, 'Firmware release');
1012
+ artifacts.push(Object.assign(Object.assign({ artifactId: 'firmware', role: 'firmware', target: 'firmware', url: assertArtifactUrl(firmwareRelease.url, 'Firmware release'), container: 'raw' }, integrity), (asVersion(firmwareRelease.version)
916
1013
  ? { targetVersion: asVersion(firmwareRelease.version) }
917
1014
  : {})));
918
1015
  }
@@ -923,53 +1020,48 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
923
1020
  platform,
924
1021
  });
925
1022
  if (resourceUrl) {
926
- artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, asIntegrity({
1023
+ const integrity = requireArtifactIntegrity({
927
1024
  size: resourceUrl === asString(firmwareRelease.fullResource)
928
1025
  ? firmwareRelease.fullResourceExpectedSize
929
1026
  : firmwareRelease.resourceExpectedSize,
930
1027
  sha256: resourceUrl === asString(firmwareRelease.fullResource)
931
1028
  ? firmwareRelease.fullResourceFingerprint
932
1029
  : firmwareRelease.resourceFingerprint,
933
- })));
1030
+ }, 'Legacy resource archive');
1031
+ artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, integrity));
934
1032
  }
935
1033
  }
936
1034
  }
937
1035
  if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
938
1036
  const bleRelease = asRelease(ble);
939
- artifacts.push(Object.assign(Object.assign({ artifactId: 'ble', role: 'ble', target: 'ble', url: assertArtifactUrl((_f = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.webUpdate) !== null && _f !== void 0 ? _f : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.url, 'BLE release'), container: 'raw' }, asIntegrity({
1037
+ const integrity = requireArtifactIntegrity({
940
1038
  size: bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.expectedSize,
941
- sha256: (_g = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.fingerprintWeb) !== null && _g !== void 0 ? _g : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.fingerprint,
942
- })), (asVersion(bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.version)
1039
+ sha256: (_a = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.fingerprintWeb) !== null && _a !== void 0 ? _a : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.fingerprint,
1040
+ }, 'BLE release');
1041
+ artifacts.push(Object.assign(Object.assign({ artifactId: 'ble', role: 'ble', target: 'ble', url: assertArtifactUrl((_b = bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.webUpdate) !== null && _b !== void 0 ? _b : bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.url, 'BLE release'), container: 'raw' }, integrity), (asVersion(bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.version)
943
1042
  ? { targetVersion: asVersion(bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.version) }
944
1043
  : {})));
945
1044
  }
946
1045
  targetsToUpdate = [...new Set(artifacts.map(artifact => artifact.target))];
947
1046
  }
1047
+ if (executor !== 'v4' &&
1048
+ targetsToUpdate.includes('resource') &&
1049
+ !targetsToUpdate.includes('firmware')) {
1050
+ planError('Legacy resource updates require a firmware target');
1051
+ }
948
1052
  assertForcedTargetsRepresented({
949
1053
  executor,
950
1054
  forcedTargets: validatedForceTargets,
951
1055
  artifacts,
952
1056
  targetsToUpdate,
953
1057
  });
954
- const reportedIdentity = getDeviceUUID(features);
955
- const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
956
- if (artifacts.length > 0 &&
957
- (platform === 'native' || platform === 'desktop') &&
958
- !deviceIdentity &&
959
- !(bootloaderMode && executor === 'v2')) {
960
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware update requires a stable device identity', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
961
- }
962
- const planWithoutDigest = {
963
- schemaVersion: 2,
964
- executor,
965
- deviceIdentity: deviceIdentity || 'unavailable',
966
- deviceModel: String(getDeviceType(features)),
1058
+ return finalizeFirmwareUpdatePlan({
1059
+ features,
967
1060
  firmwareType,
968
1061
  platform,
969
1062
  artifacts,
970
1063
  targetsToUpdate,
971
- };
972
- return assertFirmwareUpdatePlan(Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) }));
1064
+ });
973
1065
  };
974
1066
 
975
1067
  const preparedPlanError = (message) => {
@@ -1090,7 +1182,7 @@ const prepareFirmwareUpdatePlan = ({ plan: value, leaseRef, artifacts, }) => {
1090
1182
  return Object.assign(Object.assign({}, planWithoutDigest), { preparedPlanDigest: digestPreparedPlan(planWithoutDigest) });
1091
1183
  };
1092
1184
  const validateFirmwareUpdatePreparedPlan = (value) => {
1093
- var _a, _b, _c;
1185
+ var _a, _b, _c, _d;
1094
1186
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
1095
1187
  return preparedPlanError('Firmware prepared plan must be an object');
1096
1188
  }
@@ -1170,7 +1262,13 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1170
1262
  artifactIds.add(artifact.artifactId);
1171
1263
  artifactTargets.add(artifact.target);
1172
1264
  assertFirmwareArtifactReference(artifact.artifact);
1173
- (_c = artifact.materializedEntries) === null || _c === void 0 ? void 0 : _c.forEach(assertPreparedEntry);
1265
+ const materializedEntryNames = (_d = (_c = artifact.materializedEntries) === null || _c === void 0 ? void 0 : _c.map(entry => {
1266
+ assertPreparedEntry(entry);
1267
+ return getFirmwareUpdateResourceName(entry.entryName).toLowerCase();
1268
+ })) !== null && _d !== void 0 ? _d : [];
1269
+ if (new Set(materializedEntryNames).size !== materializedEntryNames.length) {
1270
+ return preparedPlanError('Firmware prepared plan contains duplicate entry names');
1271
+ }
1174
1272
  }
1175
1273
  if (preparedPlan.targetsToUpdate.some(target => !FIRMWARE_UPDATE_PLAN_TARGETS.has(target)) ||
1176
1274
  new Set(preparedPlan.targetsToUpdate).size !== preparedPlan.targetsToUpdate.length ||
@@ -1184,16 +1282,29 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1184
1282
  }
1185
1283
  return preparedPlan;
1186
1284
  };
1285
+ const getFirmwareUpdatePreparedRawArtifact = ({ preparedPlan: value, target, role, }) => {
1286
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
1287
+ const artifacts = preparedPlan.artifacts.filter(artifact => artifact.target === target);
1288
+ if (artifacts.length !== 1 || artifacts[0].role !== role || artifacts[0].container !== 'raw') {
1289
+ return preparedPlanError(`Firmware prepared plan ${target} artifact is invalid`);
1290
+ }
1291
+ return artifacts[0];
1292
+ };
1187
1293
  const degradedPlanIdentityPins = new Map();
1188
1294
  const DEGRADED_PLAN_IDENTITY_PIN_LIMIT = 32;
1189
- const pinDegradedPlanIdentity = (leaseRef, deviceIdentity) => {
1190
- if (degradedPlanIdentityPins.size >= DEGRADED_PLAN_IDENTITY_PIN_LIMIT) {
1295
+ const pinDegradedPlanIdentity = (preparedPlanDigest, deviceIdentity) => {
1296
+ const key = preparedPlanDigest.toLowerCase();
1297
+ if (!degradedPlanIdentityPins.has(key) &&
1298
+ degradedPlanIdentityPins.size >= DEGRADED_PLAN_IDENTITY_PIN_LIMIT) {
1191
1299
  const oldest = degradedPlanIdentityPins.keys().next();
1192
1300
  if (!oldest.done) {
1193
1301
  degradedPlanIdentityPins.delete(oldest.value);
1194
1302
  }
1195
1303
  }
1196
- degradedPlanIdentityPins.set(leaseRef, deviceIdentity);
1304
+ degradedPlanIdentityPins.set(key, deviceIdentity);
1305
+ };
1306
+ const clearFirmwareUpdatePreparedPlanDeviceIdentityPin = (preparedPlanDigest) => {
1307
+ degradedPlanIdentityPins.delete(preparedPlanDigest.toLowerCase());
1197
1308
  };
1198
1309
  const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, deviceIdentity: reportedIdentity, bootloaderMode, deviceModel, }) => {
1199
1310
  const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
@@ -1202,15 +1313,15 @@ const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, d
1202
1313
  if (deviceModel === undefined || preparedPlan.deviceModel !== deviceModel) {
1203
1314
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
1204
1315
  }
1205
- const pinnedIdentity = degradedPlanIdentityPins.get(preparedPlan.leaseRef);
1316
+ const pinnedIdentity = degradedPlanIdentityPins.get(preparedPlan.preparedPlanDigest.toLowerCase());
1206
1317
  if (!deviceIdentity) {
1207
- if (bootloaderMode !== true) {
1318
+ if (preparedPlan.executor !== 'v4' && bootloaderMode !== true) {
1208
1319
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
1209
1320
  }
1210
1321
  return preparedPlan;
1211
1322
  }
1212
1323
  if (!pinnedIdentity) {
1213
- pinDegradedPlanIdentity(preparedPlan.leaseRef, deviceIdentity);
1324
+ pinDegradedPlanIdentity(preparedPlan.preparedPlanDigest, deviceIdentity);
1214
1325
  return preparedPlan;
1215
1326
  }
1216
1327
  if (pinnedIdentity !== deviceIdentity) {
@@ -1275,29 +1386,40 @@ class FirmwareHostBindingRegistry {
1275
1386
  this.generation = 0;
1276
1387
  }
1277
1388
  register(binding) {
1278
- var _a;
1389
+ var _a, _b;
1279
1390
  if (!binding ||
1391
+ typeof binding.preparedPlanDigest !== 'string' ||
1392
+ !/^[0-9a-f]{64}$/i.test(binding.preparedPlanDigest) ||
1280
1393
  typeof ((_a = binding.artifactReader) === null || _a === void 0 ? void 0 : _a.open) !== 'function' ||
1281
1394
  typeof binding.artifactReader.read !== 'function' ||
1282
1395
  typeof binding.artifactReader.close !== 'function') {
1283
1396
  return bindingError('Firmware host binding is invalid');
1284
1397
  }
1398
+ const replacedPreparedPlanDigest = (_b = this.binding) === null || _b === void 0 ? void 0 : _b.preparedPlanDigest;
1285
1399
  this.generation += 1;
1286
1400
  this.binding = binding;
1401
+ if (replacedPreparedPlanDigest) {
1402
+ clearFirmwareUpdatePreparedPlanDeviceIdentityPin(replacedPreparedPlanDigest);
1403
+ }
1287
1404
  return this.generation;
1288
1405
  }
1289
1406
  unregister(generation) {
1407
+ var _a;
1290
1408
  if (generation !== undefined && generation !== this.generation) {
1291
1409
  return false;
1292
1410
  }
1411
+ const releasedPreparedPlanDigest = (_a = this.binding) === null || _a === void 0 ? void 0 : _a.preparedPlanDigest;
1293
1412
  this.generation += 1;
1294
1413
  this.binding = undefined;
1414
+ if (releasedPreparedPlanDigest) {
1415
+ clearFirmwareUpdatePreparedPlanDeviceIdentityPin(releasedPreparedPlanDigest);
1416
+ }
1295
1417
  return true;
1296
1418
  }
1297
1419
  getGeneration() {
1298
1420
  return this.generation;
1299
1421
  }
1300
- resolve(generation) {
1422
+ resolve(generation, preparedPlanDigest) {
1301
1423
  if (!Number.isSafeInteger(generation) ||
1302
1424
  generation <= 0 ||
1303
1425
  generation !== this.generation ||
@@ -1305,12 +1427,17 @@ class FirmwareHostBindingRegistry {
1305
1427
  return bindingError(`Firmware host binding generation ${generation} is stale`);
1306
1428
  }
1307
1429
  const { binding } = this;
1430
+ if (preparedPlanDigest !== undefined &&
1431
+ binding.preparedPlanDigest.toLowerCase() !== preparedPlanDigest.toLowerCase()) {
1432
+ return bindingError(`Firmware host binding generation ${generation} does not match the prepared plan`);
1433
+ }
1308
1434
  const assertCurrent = () => {
1309
1435
  if (generation !== this.generation || binding !== this.binding) {
1310
1436
  bindingError(`Firmware host binding generation ${generation} is stale`);
1311
1437
  }
1312
1438
  };
1313
1439
  return {
1440
+ preparedPlanDigest: binding.preparedPlanDigest,
1314
1441
  artifactReader: {
1315
1442
  open: (input) => __awaiter(this, void 0, void 0, function* () {
1316
1443
  assertCurrent();
@@ -1343,7 +1470,7 @@ const firmwareHostBindingRegistry = new FirmwareHostBindingRegistry();
1343
1470
  const registerFirmwareUpdateHostBinding = (binding) => firmwareHostBindingRegistry.register(binding);
1344
1471
  const unregisterFirmwareUpdateHostBinding = (generation) => firmwareHostBindingRegistry.unregister(generation);
1345
1472
  const getFirmwareUpdateHostBindingGeneration = () => firmwareHostBindingRegistry.getGeneration();
1346
- const resolveFirmwareUpdateHostBinding = (generation) => firmwareHostBindingRegistry.resolve(generation !== null && generation !== void 0 ? generation : Number.NaN);
1473
+ const resolveFirmwareUpdateHostBinding = (generation, preparedPlanDigest) => firmwareHostBindingRegistry.resolve(generation !== null && generation !== void 0 ? generation : Number.NaN, preparedPlanDigest);
1347
1474
 
1348
1475
  const callbackManager = new Map();
1349
1476
  const generateCallbackId = () => `callback_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
@@ -40504,143 +40631,8 @@ const findLatestRelease = (releases) => {
40504
40631
  return leastRelease;
40505
40632
  };
40506
40633
 
40507
- const PROTOCOL_V2_RESOURCE_TYPES = [
40508
- 'images',
40509
- 'animation',
40510
- 'wallpaper',
40511
- 'translations',
40512
- 'roobert',
40513
- 'noto',
40514
- 'firmware_logo',
40515
- ];
40516
- const PROTOCOL_V2_RESOURCE_DEVICE_PATHS = {
40517
- images: 'vol0:/bundles/images/images.okpkg',
40518
- animation: 'vol0:/bundles/images/animation.okpkg',
40519
- wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
40520
- translations: 'vol0:/bundles/translations/translations.okpkg',
40521
- roobert: 'vol0:/bundles/font/roobert.okpkg',
40522
- noto: 'vol0:/bundles/font/noto.okpkg',
40523
- firmware_logo: 'vol0:/bundles/firmware_logo.okpkg',
40524
- };
40525
- const RESOURCE_TYPE_SET = new Set(PROTOCOL_V2_RESOURCE_TYPES);
40634
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1 = 'vol0:/loaders/bootloader/boot_resource.okpkg';
40526
40635
  const SHA256_HEX_LENGTH = 64;
40527
- const SHA3_512_HEX_LENGTH = 128;
40528
- const PROTOCOL_V2_OKPP_HEADER_SIZE$1 = 0x52a0;
40529
- const PROTOCOL_V2_OKPP_TYPE_OFFSET = 0x08;
40530
- const PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET = 0x0c;
40531
- const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 = 0x240;
40532
- const PROTOCOL_V2_OKPP_HASH_SIZE$1 = 64;
40533
- const PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE = PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1;
40534
- const PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE = 64;
40535
- const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
40536
- function toFiniteNumber(value) {
40537
- if (typeof value === 'number' && Number.isFinite(value))
40538
- return value;
40539
- if (typeof value === 'string') {
40540
- const numeric = Number(value);
40541
- return Number.isFinite(numeric) ? numeric : undefined;
40542
- }
40543
- if (value && typeof value === 'object') {
40544
- const longLike = value;
40545
- if (typeof longLike.toNumber === 'function') {
40546
- const numeric = longLike.toNumber();
40547
- return Number.isFinite(numeric) ? numeric : undefined;
40548
- }
40549
- }
40550
- return undefined;
40551
- }
40552
- function toUint8Array(value) {
40553
- if (value instanceof Uint8Array)
40554
- return value;
40555
- if (value instanceof ArrayBuffer)
40556
- return new Uint8Array(value);
40557
- if (ArrayBuffer.isView(value)) {
40558
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
40559
- }
40560
- if (typeof value === 'string') {
40561
- const hex = value.replace(/^0x/i, '');
40562
- if (!hex || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
40563
- return new Uint8Array(0);
40564
- }
40565
- const bytes = new Uint8Array(hex.length / 2);
40566
- for (let index = 0; index < bytes.length; index += 1) {
40567
- bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
40568
- }
40569
- return bytes;
40570
- }
40571
- return new Uint8Array(0);
40572
- }
40573
- function readAscii(bytes, offset, length) {
40574
- return Array.from(bytes.slice(offset, offset + length), byte => String.fromCharCode(byte)).join('');
40575
- }
40576
- function parseProtocolV2ResourceHeaderHash(bytes) {
40577
- if (bytes.byteLength < PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE)
40578
- return undefined;
40579
- if (readAscii(bytes, 0, 4) !== 'OKPP')
40580
- return undefined;
40581
- if (readAscii(bytes, PROTOCOL_V2_OKPP_TYPE_OFFSET, 4) !== 'RESC')
40582
- return undefined;
40583
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
40584
- if (view.getUint32(PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET, true) !== PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
40585
- return undefined;
40586
- }
40587
- return bytesToHex$3(bytes.slice(PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1, PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1));
40588
- }
40589
- function readProtocolV2ResourceIdentity({ commands, resource, chunkSize, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
40590
- var _a, _b, _c, _d;
40591
- return __awaiter(this, void 0, void 0, function* () {
40592
- const path = PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type];
40593
- const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path }, { timeoutMs });
40594
- const size = toFiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
40595
- if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
40596
- ((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
40597
- !Number.isSafeInteger(size) ||
40598
- size !== resource.size ||
40599
- size < PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
40600
- return undefined;
40601
- }
40602
- const header = new Uint8Array(PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE);
40603
- let offset = 0;
40604
- while (offset < header.byteLength) {
40605
- const readLength = Math.min(chunkSize, header.byteLength - offset);
40606
- const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
40607
- file: { path, offset, total_size: 0 },
40608
- chunk_len: readLength,
40609
- }, { timeoutMs });
40610
- const data = toUint8Array((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
40611
- if (data.byteLength === 0)
40612
- return undefined;
40613
- const copied = Math.min(data.byteLength, header.byteLength - offset);
40614
- header.set(data.subarray(0, copied), offset);
40615
- offset += copied;
40616
- }
40617
- const headerHash = parseProtocolV2ResourceHeaderHash(header);
40618
- return headerHash ? { type: resource.type, size, headerHash } : undefined;
40619
- });
40620
- }
40621
- function readProtocolV2ResourceInventory({ commands, resources, chunkSize = hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
40622
- return __awaiter(this, void 0, void 0, function* () {
40623
- const normalizedChunkSize = Number.isFinite(chunkSize)
40624
- ? Math.max(Math.floor(chunkSize), PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE)
40625
- : hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
40626
- const inventory = [];
40627
- for (const resource of resources) {
40628
- try {
40629
- const item = yield readProtocolV2ResourceIdentity({
40630
- commands,
40631
- resource,
40632
- chunkSize: normalizedChunkSize,
40633
- timeoutMs,
40634
- });
40635
- if (item)
40636
- inventory.push(item);
40637
- }
40638
- catch (_a) {
40639
- }
40640
- }
40641
- return inventory;
40642
- });
40643
- }
40644
40636
  function normalizeHex$1(value, expectedLength, field) {
40645
40637
  if (typeof value !== 'string') {
40646
40638
  throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
@@ -40651,132 +40643,158 @@ function normalizeHex$1(value, expectedLength, field) {
40651
40643
  }
40652
40644
  return normalized;
40653
40645
  }
40654
- function validateResource(value, index) {
40646
+ function parseProtocolV2Resources(value) {
40647
+ if (value === undefined)
40648
+ return undefined;
40655
40649
  if (!value || typeof value !== 'object') {
40656
- throw new Error(`Invalid Pro2 resource at stable[${index}]`);
40650
+ throw new Error('Invalid Pro2 resources config');
40651
+ }
40652
+ const { source } = value;
40653
+ if (!source || typeof source !== 'object') {
40654
+ throw new Error('Invalid Pro2 resources config: source is required');
40657
40655
  }
40658
- const resource = value;
40659
- if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
40660
- throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
40656
+ const { archiveUrl, archiveSha256, archiveSize } = source;
40657
+ if (typeof archiveUrl !== 'string' || !archiveUrl.startsWith('https://')) {
40658
+ throw new Error('Invalid Pro2 resources config: source.archiveUrl must use HTTPS');
40661
40659
  }
40662
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
40663
- throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
40660
+ if (typeof archiveSha256 !== 'string' || !/^[0-9a-fA-F]{64}$/.test(archiveSha256)) {
40661
+ throw new Error('Invalid Pro2 resources config: source.archiveSha256 must be a SHA-256 digest');
40664
40662
  }
40665
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
40666
- throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
40663
+ if (!Number.isSafeInteger(archiveSize) || archiveSize <= 0) {
40664
+ throw new Error('Invalid Pro2 resources config: source.archiveSize must be a positive integer');
40667
40665
  }
40668
40666
  return {
40669
- type: resource.type,
40670
- url: resource.url,
40671
- size: Number(resource.size),
40672
- fileHash: normalizeHex$1(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
40673
- headerHash: normalizeHex$1(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
40667
+ source: {
40668
+ archiveUrl,
40669
+ archiveSha256: archiveSha256.toLowerCase(),
40670
+ archiveSize: archiveSize,
40671
+ },
40674
40672
  };
40675
40673
  }
40676
- function validateBootResources(value) {
40677
- if (!value || typeof value !== 'object') {
40678
- throw new Error('Invalid Pro2 boot resources config');
40674
+ const PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS = [
40675
+ 'vol0:/bundles/',
40676
+ 'vol0:/loaders/rom/',
40677
+ ];
40678
+ function isAllowedManifestDevicePath(path) {
40679
+ if (!path.endsWith('.okpkg') ||
40680
+ path.includes('\\') ||
40681
+ path.includes('//') ||
40682
+ path.split('/').some(part => part === '.' || part === '..')) {
40683
+ return false;
40679
40684
  }
40680
- const resource = value;
40681
- if (resource.required !== false) {
40682
- throw new Error('Invalid Pro2 boot resources required flag: expected false');
40685
+ if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) {
40686
+ return true;
40683
40687
  }
40684
- if (resource.target !== 'RES') {
40685
- throw new Error('Invalid Pro2 boot resources target: expected RES');
40688
+ return PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS.some(root => path.startsWith(root));
40689
+ }
40690
+ function assertManifestString(value, field) {
40691
+ if (typeof value !== 'string' || value.length === 0) {
40692
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
40686
40693
  }
40687
- if (resource.manifestUrl !== undefined &&
40688
- (typeof resource.manifestUrl !== 'string' || !resource.manifestUrl.startsWith('https://'))) {
40689
- throw new Error('Invalid Pro2 boot resources manifestUrl');
40694
+ return value;
40695
+ }
40696
+ function assertManifestRelativePath(value, field) {
40697
+ const path = assertManifestString(value, field);
40698
+ if (path.startsWith('/') ||
40699
+ path.includes('\\') ||
40700
+ path.includes(':') ||
40701
+ path.split('/').some(part => !part || part === '.' || part === '..')) {
40702
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
40703
+ }
40704
+ return path;
40705
+ }
40706
+ function parseProtocolV2ResourceManifestFile(value, index) {
40707
+ var _a;
40708
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
40709
+ throw new Error(`Invalid Pro2 resource manifest files[${index}]`);
40690
40710
  }
40691
- if (!Array.isArray(resource.files) || resource.files.length === 0) {
40692
- throw new Error('Invalid Pro2 boot resources files');
40711
+ const file = value;
40712
+ const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
40713
+ const originalName = assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
40714
+ if (originalName.includes('/')) {
40715
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
40693
40716
  }
40694
- const files = resource.files.map((value, index) => {
40695
- if (!value || typeof value !== 'object') {
40696
- throw new Error(`Invalid Pro2 boot resource file at files[${index}]`);
40697
- }
40698
- const file = value;
40699
- if (typeof file.url !== 'string' || !file.url.startsWith('https://')) {
40700
- throw new Error(`Invalid Pro2 boot resource url at files[${index}]`);
40701
- }
40702
- if (typeof file.devicePath !== 'string' ||
40703
- !file.devicePath.startsWith('vol0:/') ||
40704
- file.devicePath.includes('..') ||
40705
- file.devicePath.includes('\\')) {
40706
- throw new Error(`Invalid Pro2 boot resource devicePath at files[${index}]`);
40707
- }
40708
- if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
40709
- throw new Error(`Invalid Pro2 boot resource size at files[${index}]`);
40710
- }
40711
- return Object.assign(Object.assign({}, (typeof file.name === 'string' && file.name ? { name: file.name } : undefined)), { url: file.url, devicePath: file.devicePath, size: Number(file.size), fileHash: normalizeHex$1(file.fileHash, SHA256_HEX_LENGTH, `boot files[${index}].fileHash`) });
40712
- });
40713
- if (new Set(files.map(file => file.devicePath)).size !== files.length) {
40714
- throw new Error('Invalid Pro2 boot resources files: duplicate devicePath');
40717
+ const devicePath = assertManifestString(file.device_path, `files[${index}].device_path`);
40718
+ if (!isAllowedManifestDevicePath(devicePath)) {
40719
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].device_path`);
40715
40720
  }
40716
- return Object.assign(Object.assign({ required: false, target: 'RES' }, (resource.manifestUrl ? { manifestUrl: resource.manifestUrl } : undefined)), { files });
40717
- }
40718
- function parseProtocolV2Resources(value) {
40719
- if (value === undefined)
40720
- return undefined;
40721
- if (!value ||
40722
- typeof value !== 'object' ||
40723
- !Array.isArray(value.stable)) {
40724
- throw new Error('Invalid Pro2 resources config: stable must be an array');
40721
+ if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
40722
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
40725
40723
  }
40726
- const config = value;
40727
- const stable = config.stable.map(validateResource);
40728
- const types = new Set(stable.map(resource => resource.type));
40729
- if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
40730
- throw new Error(`Invalid Pro2 resources config: stable must contain ${PROTOCOL_V2_RESOURCE_TYPES.length} unique resource types`);
40724
+ const digest = normalizeHex$1(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
40725
+ if (file.signed !== true) {
40726
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].signed`);
40731
40727
  }
40732
- for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
40733
- if (!types.has(type)) {
40734
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
40735
- }
40728
+ if (file.sig_algo !== 'ed25519' && file.sig_algo !== 'mldsa65') {
40729
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].sig_algo`);
40736
40730
  }
40737
- const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
40738
- return Object.assign({ stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
40739
- const resource = stable.find(item => item.type === type);
40740
- if (!resource) {
40741
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
40742
- }
40743
- return resource;
40744
- }) }, (boot ? { boot } : undefined));
40745
- }
40746
- function buildProtocolV2ResourceUpdatePlan({ resources, inventory, mode, forced = false, }) {
40747
- if (forced) {
40748
- return {
40749
- status: resources.length > 0 ? 'outdated' : 'valid',
40750
- resources: [...resources],
40751
- };
40731
+ if (file.payload_version !== null && typeof file.payload_version !== 'string') {
40732
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].payload_version`);
40752
40733
  }
40753
- if (!inventory) {
40754
- return mode === 'bootloader-recovery'
40755
- ? {
40756
- status: resources.length > 0 ? 'outdated' : 'valid',
40757
- resources: [...resources],
40758
- }
40759
- : { status: 'unknown', resources: [] };
40734
+ if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
40735
+ throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
40760
40736
  }
40761
- const inventoryByType = new Map(inventory.map(item => [item.type, item]));
40762
- const changedResources = resources.filter(resource => {
40763
- const current = inventoryByType.get(resource.type);
40764
- return (!current ||
40765
- current.size !== resource.size ||
40766
- current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase());
40767
- });
40768
40737
  return {
40769
- status: changedResources.length === 0 ? 'valid' : 'outdated',
40770
- resources: changedResources,
40738
+ archive_path: archivePath,
40739
+ original_name: originalName,
40740
+ device_path: devicePath,
40741
+ size: Number(file.size),
40742
+ sha256: digest,
40743
+ signed: true,
40744
+ sig_algo: file.sig_algo,
40745
+ payload_version: (_a = file.payload_version) !== null && _a !== void 0 ? _a : null,
40771
40746
  };
40772
40747
  }
40773
- function bytesToHex$3(bytes) {
40774
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
40748
+ function parseProtocolV2ResourceManifest(value) {
40749
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
40750
+ throw new Error('Invalid Pro2 resource manifest');
40751
+ }
40752
+ const manifest = value;
40753
+ if (manifest.schema !== 1 ||
40754
+ manifest.variant !== 'resource' ||
40755
+ manifest.device_root !== 'vol0:' ||
40756
+ manifest.restore_mode !== 'bootloader_update' ||
40757
+ !Array.isArray(manifest.trees) ||
40758
+ !Array.isArray(manifest.files)) {
40759
+ throw new Error('Invalid Pro2 resource manifest contract');
40760
+ }
40761
+ const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
40762
+ const devicePaths = new Set(files.map(file => file.device_path));
40763
+ const archivePaths = new Set(files.map(file => file.archive_path));
40764
+ if (files.length === 0 ||
40765
+ devicePaths.size !== files.length ||
40766
+ archivePaths.size !== files.length ||
40767
+ !devicePaths.has(PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) ||
40768
+ !files.some(file => file.device_path.startsWith('vol0:/bundles/'))) {
40769
+ throw new Error('Invalid Pro2 resource manifest file set');
40770
+ }
40771
+ return {
40772
+ schema: 1,
40773
+ artifact_name: assertManifestString(manifest.artifact_name, 'artifact_name'),
40774
+ release_name: assertManifestString(manifest.release_name, 'release_name'),
40775
+ variant: 'resource',
40776
+ commit: assertManifestString(manifest.commit, 'commit'),
40777
+ short_sha: assertManifestString(manifest.short_sha, 'short_sha'),
40778
+ timestamp_utc: assertManifestString(manifest.timestamp_utc, 'timestamp_utc'),
40779
+ core_version: assertManifestString(manifest.core_version, 'core_version'),
40780
+ key_set: assertManifestString(manifest.key_set, 'key_set'),
40781
+ device_root: 'vol0:',
40782
+ restore_mode: 'bootloader_update',
40783
+ trees: manifest.trees.map((tree, index) => {
40784
+ if (!tree || typeof tree !== 'object') {
40785
+ throw new Error(`Invalid Pro2 resource manifest trees[${index}]`);
40786
+ }
40787
+ const item = tree;
40788
+ return {
40789
+ path: assertManifestRelativePath(item.path, `trees[${index}].path`),
40790
+ device: assertManifestString(item.device, `trees[${index}].device`),
40791
+ };
40792
+ }),
40793
+ files,
40794
+ };
40775
40795
  }
40776
- function isProtocolV2ResourceFileValid(binary, resource) {
40777
- if (binary.byteLength !== resource.size)
40778
- return false;
40779
- return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
40796
+ function selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate, }) {
40797
+ return targetsToUpdate.includes('resource') ? [...manifest.files] : [];
40780
40798
  }
40781
40799
 
40782
40800
  var _a$1;
@@ -40837,6 +40855,7 @@ class DataManager {
40837
40855
  return __awaiter(this, void 0, void 0, function* () {
40838
40856
  this.settings = settings;
40839
40857
  this.protocolV2ResourcesConfigError = undefined;
40858
+ this.protocolV2NeoResourcesConfigError = undefined;
40840
40859
  const manifestMode = (_b = settings.firmwareManifestMode) !== null && _b !== void 0 ? _b : (settings.fetchConfig ? 'sdk-managed' : undefined);
40841
40860
  if (settings.preloadedConfig) {
40842
40861
  this.applyRemoteConfig(settings.preloadedConfig);
@@ -40897,12 +40916,19 @@ class DataManager {
40897
40916
  let neoResources;
40898
40917
  try {
40899
40918
  pro2Resources = parseProtocolV2Resources((_b = data.pro2) === null || _b === void 0 ? void 0 : _b.resources);
40900
- neoResources = parseProtocolV2Resources((_c = data.neo) === null || _c === void 0 ? void 0 : _c.resources);
40901
40919
  }
40902
40920
  catch (error) {
40903
40921
  this.protocolV2ResourcesConfigError =
40904
40922
  error instanceof Error ? error : new Error(String(error));
40905
- Log$k.warn('[DataConfig] Ignoring invalid Protocol V2 resources config:', error);
40923
+ Log$k.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
40924
+ }
40925
+ try {
40926
+ neoResources = parseProtocolV2Resources((_c = data.neo) === null || _c === void 0 ? void 0 : _c.resources);
40927
+ }
40928
+ catch (error) {
40929
+ this.protocolV2NeoResourcesConfigError =
40930
+ error instanceof Error ? error : new Error(String(error));
40931
+ Log$k.warn('[DataConfig] Ignoring invalid Neo resources config:', error);
40906
40932
  }
40907
40933
  const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
40908
40934
  const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
@@ -40940,16 +40966,20 @@ class DataManager {
40940
40966
  }
40941
40967
  });
40942
40968
  }
40943
- static forceReloadData({ requireResources = false, } = {}) {
40969
+ static forceReloadData({ requireResources = false, resourceDeviceType = hdShared.EDeviceType.Pro2, } = {}) {
40944
40970
  return __awaiter(this, void 0, void 0, function* () {
40971
+ const resourceDeviceName = resourceDeviceType === hdShared.EDeviceType.Pro2 ? 'Pro2' : 'Neo';
40945
40972
  if (!this.settings) {
40946
40973
  throw new Error('Remote config settings are not initialized');
40947
40974
  }
40948
40975
  const hasFreshConfig = this.lastCheckTimestamp > 0 &&
40949
40976
  getTimeStamp() - this.lastCheckTimestamp <= FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS;
40950
40977
  if (hasFreshConfig) {
40951
- if (requireResources && this.protocolV2ResourcesConfigError) {
40952
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`);
40978
+ const resourcesConfigError = resourceDeviceType === hdShared.EDeviceType.Pro2
40979
+ ? this.protocolV2ResourcesConfigError
40980
+ : this.protocolV2NeoResourcesConfigError;
40981
+ if (requireResources && resourcesConfigError) {
40982
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`);
40953
40983
  }
40954
40984
  return;
40955
40985
  }
@@ -40957,19 +40987,18 @@ class DataManager {
40957
40987
  if (!loaded) {
40958
40988
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Unable to refresh the latest remote config');
40959
40989
  }
40960
- if (requireResources && this.protocolV2ResourcesConfigError) {
40961
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`);
40990
+ const resourcesConfigError = resourceDeviceType === hdShared.EDeviceType.Pro2
40991
+ ? this.protocolV2ResourcesConfigError
40992
+ : this.protocolV2NeoResourcesConfigError;
40993
+ if (requireResources && resourcesConfigError) {
40994
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`);
40962
40995
  }
40963
40996
  this.lastCheckTimestamp = getTimeStamp();
40964
40997
  });
40965
40998
  }
40966
- static getProtocolV2Resources(deviceType = hdShared.EDeviceType.Pro2) {
40967
- var _b, _c;
40968
- return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.stable;
40969
- }
40970
- static getProtocolV2BootResources(deviceType = hdShared.EDeviceType.Pro2) {
40999
+ static getProtocolV2ResourceSource(deviceType = hdShared.EDeviceType.Pro2) {
40971
41000
  var _b, _c;
40972
- return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.boot;
41001
+ return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.source;
40973
41002
  }
40974
41003
  static getProtobufMessages(schema = 'v1CurrentSchema') {
40975
41004
  return this.messages[schema];
@@ -41006,12 +41035,10 @@ DataManager.deviceMap = {
41006
41035
  ble: [],
41007
41036
  },
41008
41037
  [hdShared.EDeviceType.Pro2]: {
41009
- firmware: [],
41010
- ble: [],
41038
+ 'firmware-v1': [],
41011
41039
  },
41012
41040
  [hdShared.EDeviceType.Neo]: {
41013
- firmware: [],
41014
- ble: [],
41041
+ 'firmware-v1': [],
41015
41042
  },
41016
41043
  [hdShared.EDeviceType.ClassicPure]: {
41017
41044
  firmware: [],
@@ -42008,7 +42035,7 @@ const getFirmwareUpdateField = ({ features, updateType, targetVersion, firmwareT
42008
42035
  if (deviceType === hdShared.EDeviceType.Pro) {
42009
42036
  return latestFirmwareField;
42010
42037
  }
42011
- if (deviceType === hdShared.EDeviceType.Pro2) {
42038
+ if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo) {
42012
42039
  return 'firmware-v1';
42013
42040
  }
42014
42041
  return 'firmware';
@@ -42040,7 +42067,7 @@ const getFirmwareUpdateFieldArray = (features, updateType) => {
42040
42067
  if (deviceType === 'pro') {
42041
42068
  return ['firmware-v8'];
42042
42069
  }
42043
- if (deviceType === 'pro2') {
42070
+ if (deviceType === 'pro2' || deviceType === 'neo') {
42044
42071
  return ['firmware-v1'];
42045
42072
  }
42046
42073
  return ['firmware'];
@@ -47383,76 +47410,93 @@ class ClearSessionCache extends BaseMethod {
47383
47410
  }
47384
47411
  }
47385
47412
 
47386
- class CheckFirmwareRelease extends BaseMethod {
47387
- init() {
47388
- this.allowDeviceMode = [
47389
- ...this.allowDeviceMode,
47390
- UI_REQUEST.NOT_INITIALIZE,
47391
- UI_REQUEST.BOOTLOADER,
47392
- ];
47393
- this.useDevicePassphraseState = false;
47394
- this.skipForceUpdateCheck = true;
47395
- }
47396
- run() {
47397
- var _a;
47398
- const payload = this.payload;
47399
- if (this.device.features) {
47400
- const deviceFirmwareType = this.device.getCurrentFirmwareType();
47401
- const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
47402
- const releaseInfo = getFirmwareReleaseInfo(this.device.features, firmwareType);
47403
- return Promise.resolve(releaseInfo);
47413
+ const PROTOCOL_V2_MAIN_FIRMWARE_TARGETS = [
47414
+ 'APPLICATION_P1',
47415
+ 'APPLICATION_P2',
47416
+ 'SE01',
47417
+ 'SE02',
47418
+ 'SE03',
47419
+ 'SE04',
47420
+ ];
47421
+ const PROTOCOL_V2_BLE_TARGETS = [
47422
+ 'COPROCESSOR',
47423
+ ];
47424
+ const PROTOCOL_V2_BOOTLOADER_TARGETS = [
47425
+ 'BOOTLOADER',
47426
+ ];
47427
+ function loadProtocolV2FirmwareReleaseContext({ device, firmwareType: firmwareTypeParam, checkFirmwareHash = false, methodName, }) {
47428
+ var _a;
47429
+ return __awaiter(this, void 0, void 0, function* () {
47430
+ const state = yield device.getDeviceState({
47431
+ refreshSections: checkFirmwareHash
47432
+ ? ['identity', 'versions', 'verification']
47433
+ : ['identity', 'versions'],
47434
+ });
47435
+ if (state.identity.deviceType !== 'pro2' && state.identity.deviceType !== 'neo') {
47436
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotSupportMethod, `${methodName} requires a Pro2 or Neo device for Protocol V2`);
47404
47437
  }
47405
- return Promise.resolve(null);
47406
- }
47438
+ const { features } = device;
47439
+ if (!features) {
47440
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `${methodName} requires initialized device features`);
47441
+ }
47442
+ const firmwareType = (_a = firmwareTypeParam !== null && firmwareTypeParam !== void 0 ? firmwareTypeParam : state.identity.firmwareType) !== null && _a !== void 0 ? _a : hdShared.EFirmwareType.Universal;
47443
+ return {
47444
+ state: state,
47445
+ features,
47446
+ firmwareType,
47447
+ release: DataManager.getFirmwareLatestRelease(features, firmwareType),
47448
+ };
47449
+ });
47407
47450
  }
47408
-
47409
- class CheckBLEFirmwareRelease extends BaseMethod {
47410
- init() {
47411
- this.allowDeviceMode = [
47412
- ...this.allowDeviceMode,
47413
- UI_REQUEST.NOT_INITIALIZE,
47414
- UI_REQUEST.BOOTLOADER,
47415
- ];
47416
- this.useDevicePassphraseState = false;
47417
- this.skipForceUpdateCheck = true;
47451
+ function summarizeProtocolV2FirmwareRelease(plan, componentTargets) {
47452
+ var _a;
47453
+ const targetSet = new Set(componentTargets);
47454
+ const components = plan.components.filter(component => targetSet.has(component.componentTarget));
47455
+ const targetsToUpdate = Array.from(new Set(components.flatMap(component => component.status === 'outdated' && component.updateTarget ? [component.updateTarget] : [])));
47456
+ const hasUpgrade = targetsToUpdate.length > 0;
47457
+ const required = !!((_a = plan.release) === null || _a === void 0 ? void 0 : _a.required) && hasUpgrade;
47458
+ let status = 'valid';
47459
+ if (!plan.release || components.length === 0) {
47460
+ status = 'unavailable';
47418
47461
  }
47419
- run() {
47420
- if (this.device.features) {
47421
- const releaseInfo = getBleFirmwareReleaseInfo(this.device.features);
47422
- return Promise.resolve(releaseInfo);
47423
- }
47424
- return Promise.resolve(null);
47462
+ else if (required) {
47463
+ status = 'required';
47464
+ }
47465
+ else if (hasUpgrade) {
47466
+ status = 'outdated';
47467
+ }
47468
+ else if (components.some(component => component.status === 'unknown')) {
47469
+ status = 'unknown';
47470
+ }
47471
+ else if (components.every(component => component.status === 'unsupported')) {
47472
+ status = 'unavailable';
47425
47473
  }
47474
+ return Object.assign(Object.assign({}, plan), { status,
47475
+ hasUpgrade,
47476
+ required,
47477
+ components,
47478
+ targetsToUpdate });
47426
47479
  }
47427
-
47428
- class CheckBridgeStatus extends BaseMethod {
47429
- init() {
47430
- this.useDevice = false;
47431
- this.useDevicePassphraseState = false;
47432
- this.skipForceUpdateCheck = true;
47480
+ function toProtocolV2FirmwareReleaseInfo({ plan, state, release, }) {
47481
+ return {
47482
+ shouldUpdate: plan.hasUpgrade,
47483
+ status: plan.status === 'unavailable' ? 'unknown' : plan.status,
47484
+ changelog: plan.release ? [plan.release.changelog] : [],
47485
+ release,
47486
+ bootloaderMode: state.status.mode === 'bootloader' || state.status.mode === 'romloader',
47487
+ };
47488
+ }
47489
+ function getProtocolV2ComponentReleaseInfo(plan, componentTarget) {
47490
+ var _a;
47491
+ const componentRelease = plan.components.find(component => component.componentTarget === componentTarget);
47492
+ if (!componentRelease || !plan.release) {
47493
+ return undefined;
47433
47494
  }
47434
- run() {
47435
- return __awaiter(this, void 0, void 0, function* () {
47436
- return new Promise((resolve, reject) => {
47437
- axios__default["default"]
47438
- .request({
47439
- url: 'http://localhost:21320',
47440
- method: 'POST',
47441
- withCredentials: false,
47442
- timeout: 3000,
47443
- })
47444
- .then(() => resolve(true))
47445
- .catch(e => {
47446
- if (e.code === 'ECONNABORTED') {
47447
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BridgeTimeoutError));
47448
- }
47449
- else {
47450
- resolve(false);
47451
- }
47452
- });
47453
- });
47454
- });
47495
+ const component = (_a = plan.release.components) === null || _a === void 0 ? void 0 : _a[componentRelease.configKey];
47496
+ if (!component) {
47497
+ return undefined;
47455
47498
  }
47499
+ return Object.assign(Object.assign({}, component), { protocol: 'V2', configKey: componentRelease.configKey, componentTarget, required: plan.release.required, changelog: plan.release.changelog });
47456
47500
  }
47457
47501
 
47458
47502
  const REQUIRED_BRIDGE_VERSION = '2.2.0';
@@ -47509,56 +47553,6 @@ function getBridgeReleaseInfo({ deviceType, currentFirmwareVersion, willUpdateFi
47509
47553
  });
47510
47554
  }
47511
47555
 
47512
- class CheckBridgeRelease extends BaseMethod {
47513
- init() {
47514
- this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
47515
- this.useDevicePassphraseState = false;
47516
- this.skipForceUpdateCheck = true;
47517
- }
47518
- run() {
47519
- return __awaiter(this, void 0, void 0, function* () {
47520
- if (!this.device.features) {
47521
- return null;
47522
- }
47523
- const { willUpdateFirmwareVersion } = this.payload;
47524
- const { features } = this.device;
47525
- const deviceType = getDeviceType(features);
47526
- const currentFirmwareVersion = getDeviceFirmwareVersion(features).join('.');
47527
- return getBridgeReleaseInfo({
47528
- deviceType,
47529
- currentFirmwareVersion,
47530
- willUpdateFirmwareVersion,
47531
- });
47532
- });
47533
- }
47534
- }
47535
-
47536
- class CheckBootloaderRelease extends BaseMethod {
47537
- init() {
47538
- this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
47539
- this.useDevicePassphraseState = false;
47540
- this.skipForceUpdateCheck = true;
47541
- }
47542
- run() {
47543
- var _a;
47544
- return __awaiter(this, void 0, void 0, function* () {
47545
- if (!this.device.features) {
47546
- return null;
47547
- }
47548
- const { features } = this.device;
47549
- const payload = this.payload;
47550
- const deviceFirmwareType = this.device.getCurrentFirmwareType();
47551
- const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
47552
- const releaseInfo = getBootloaderReleaseInfo({
47553
- features,
47554
- willUpdateFirmwareVersion: payload.willUpdateFirmwareVersion,
47555
- firmwareType,
47556
- });
47557
- return Promise.resolve(releaseInfo);
47558
- });
47559
- }
47560
- }
47561
-
47562
47556
  const UPDATE_TARGET_BY_COMPONENT_TARGET = {
47563
47557
  BOOTLOADER: 'boot',
47564
47558
  APPLICATION_P1: 'app_v1',
@@ -47806,22 +47800,15 @@ class CheckAllFirmwareRelease extends BaseMethod {
47806
47800
  runProtocolV2() {
47807
47801
  var _a;
47808
47802
  return __awaiter(this, void 0, void 0, function* () {
47809
- const { checkFirmwareHash = false, firmwareType: firmwareTypeParam } = this
47810
- .payload;
47811
- const state = yield this.device.getDeviceState({
47812
- refreshSections: checkFirmwareHash
47813
- ? ['identity', 'versions', 'verification']
47814
- : ['identity', 'versions'],
47803
+ const { checkFirmwareHash = false, firmwareType: firmwareTypeParam, platform, forceUpdateTargets, protocolV2ForceUpdateTargets, } = this.payload;
47804
+ const validatedForceUpdateTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
47805
+ const validatedProtocolV2ForceUpdateTargets = validateProtocolV2FirmwareUpdateTargets(protocolV2ForceUpdateTargets);
47806
+ const { state, features, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47807
+ device: this.device,
47808
+ firmwareType: firmwareTypeParam,
47809
+ checkFirmwareHash,
47810
+ methodName: 'checkAllFirmwareRelease',
47815
47811
  });
47816
- if (state.identity.deviceType !== 'pro2' && state.identity.deviceType !== 'neo') {
47817
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotSupportMethod, 'checkAllFirmwareRelease requires a Pro2 or Neo device for Protocol V2');
47818
- }
47819
- const { features } = this.device;
47820
- if (!features) {
47821
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'checkAllFirmwareRelease requires initialized device features');
47822
- }
47823
- const firmwareType = (_a = firmwareTypeParam !== null && firmwareTypeParam !== void 0 ? firmwareTypeParam : state.identity.firmwareType) !== null && _a !== void 0 ? _a : hdShared.EFirmwareType.Universal;
47824
- const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
47825
47812
  const plan = buildProtocolV2FirmwareRelease({
47826
47813
  currentVersions: state.versions,
47827
47814
  currentVerification: checkFirmwareHash ? state.verification : undefined,
@@ -47831,48 +47818,251 @@ class CheckAllFirmwareRelease extends BaseMethod {
47831
47818
  deviceType: state.identity.deviceType,
47832
47819
  });
47833
47820
  const resourceDeviceType = state.identity.deviceType === 'neo' ? hdShared.EDeviceType.Neo : hdShared.EDeviceType.Pro2;
47834
- const resources = DataManager.getProtocolV2Resources(resourceDeviceType);
47835
- let resourceStatus = 'unknown';
47836
- if (resources === null || resources === void 0 ? void 0 : resources.length) {
47837
- const loaderMode = state.status.mode === 'bootloader' || state.status.mode === 'romloader';
47838
- if (loaderMode) {
47839
- try {
47840
- const inventory = yield readProtocolV2ResourceInventory({
47841
- commands: this.device.getCommands(),
47842
- resources,
47843
- });
47844
- resourceStatus = buildProtocolV2ResourceUpdatePlan({
47845
- resources,
47846
- inventory,
47847
- mode: 'bootloader-recovery',
47848
- }).status;
47821
+ const resourceSource = DataManager.getProtocolV2ResourceSource(resourceDeviceType);
47822
+ const resourceStatus = 'unknown';
47823
+ const detectedComponentTargets = validatedForceUpdateTargets.includes('firmware')
47824
+ ? plan.components.flatMap(component => component.updateTarget ? [component.updateTarget] : [])
47825
+ : plan.targetsToUpdate;
47826
+ const componentTargetsToUpdate = Array.from(new Set([
47827
+ ...detectedComponentTargets,
47828
+ ...validatedProtocolV2ForceUpdateTargets.filter(target => target !== 'resource'),
47829
+ ]));
47830
+ const forceResourceUpdate = validatedForceUpdateTargets.includes('resource') ||
47831
+ validatedProtocolV2ForceUpdateTargets.includes('resource');
47832
+ const includeResourceUpdate = forceResourceUpdate || (resourceSource !== undefined && detectedComponentTargets.length > 0);
47833
+ const targetsToUpdate = Array.from(new Set([
47834
+ ...componentTargetsToUpdate,
47835
+ ...(includeResourceUpdate ? ['resource'] : []),
47836
+ ]));
47837
+ let firmwareUpdatePlan;
47838
+ const shouldBuildFirmwareUpdatePlan = targetsToUpdate.length > 0;
47839
+ const requestedPlatform = platform !== null && platform !== void 0 ? platform : 'web';
47840
+ const requiresPreparedPlan = shouldBuildFirmwareUpdatePlan &&
47841
+ (requestedPlatform === 'native' || requestedPlatform === 'desktop');
47842
+ try {
47843
+ if (shouldBuildFirmwareUpdatePlan) {
47844
+ const validatedPlan = buildProtocolV2FirmwareUpdatePlan({
47845
+ features,
47846
+ firmwareType,
47847
+ platform: requestedPlatform,
47848
+ release,
47849
+ targetsToUpdate,
47850
+ forceUpdateTargets: validatedForceUpdateTargets,
47851
+ resourceArchive: resourceSource,
47852
+ });
47853
+ firmwareUpdatePlan = validatedPlan;
47854
+ }
47855
+ else {
47856
+ firmwareUpdatePlan = undefined;
47857
+ }
47858
+ }
47859
+ catch (error) {
47860
+ if (requiresPreparedPlan ||
47861
+ validatedForceUpdateTargets.length > 0 ||
47862
+ validatedProtocolV2ForceUpdateTargets.length > 0 ||
47863
+ !(error instanceof hdShared.HardwareError) ||
47864
+ ((_a = error.params) === null || _a === void 0 ? void 0 : _a.firmwareUpdateCode) !== 'FirmwarePlanInvalid') {
47865
+ throw error;
47866
+ }
47867
+ Log$c.warn('[CheckAllFirmwareRelease] Optional Protocol V2 firmware Plan is unavailable; using the release result');
47868
+ firmwareUpdatePlan = undefined;
47869
+ }
47870
+ const firmwarePlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_MAIN_FIRMWARE_TARGETS);
47871
+ const blePlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_BLE_TARGETS);
47872
+ const bootloaderPlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_BOOTLOADER_TARGETS);
47873
+ const { status } = plan;
47874
+ return Object.assign(Object.assign({ firmware: toProtocolV2FirmwareReleaseInfo({ plan: firmwarePlan, state, release }), ble: toProtocolV2FirmwareReleaseInfo({
47875
+ plan: blePlan,
47876
+ state,
47877
+ release: getProtocolV2ComponentReleaseInfo(plan, 'COPROCESSOR'),
47878
+ }), bootloader: toProtocolV2FirmwareReleaseInfo({
47879
+ plan: bootloaderPlan,
47880
+ state,
47881
+ release: getProtocolV2ComponentReleaseInfo(plan, 'BOOTLOADER'),
47882
+ }), features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { status,
47883
+ resourceStatus, resourceArchive: resourceSource, hasUpgrade: targetsToUpdate.length > 0, targetsToUpdate,
47884
+ firmwareUpdatePlan });
47885
+ });
47886
+ }
47887
+ }
47888
+
47889
+ class CheckFirmwareRelease extends BaseMethod {
47890
+ getSupportedProtocols() {
47891
+ return ['V1', 'V2'];
47892
+ }
47893
+ init() {
47894
+ this.allowDeviceMode = [
47895
+ ...this.allowDeviceMode,
47896
+ UI_REQUEST.NOT_INITIALIZE,
47897
+ UI_REQUEST.BOOTLOADER,
47898
+ ];
47899
+ this.useDevicePassphraseState = false;
47900
+ this.skipForceUpdateCheck = true;
47901
+ }
47902
+ run() {
47903
+ var _a;
47904
+ return __awaiter(this, void 0, void 0, function* () {
47905
+ const payload = this.payload;
47906
+ if (!this.device.features)
47907
+ return null;
47908
+ if (this.device.isProtocolV2()) {
47909
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47910
+ device: this.device,
47911
+ firmwareType: payload.firmwareType,
47912
+ methodName: 'checkFirmwareRelease',
47913
+ });
47914
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
47915
+ currentVersions: state.versions,
47916
+ firmwareType,
47917
+ release,
47918
+ deviceType: state.identity.deviceType,
47919
+ }), PROTOCOL_V2_MAIN_FIRMWARE_TARGETS);
47920
+ return toProtocolV2FirmwareReleaseInfo({ plan, state, release });
47921
+ }
47922
+ const deviceFirmwareType = this.device.getCurrentFirmwareType();
47923
+ const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
47924
+ const releaseInfo = getFirmwareReleaseInfo(this.device.features, firmwareType);
47925
+ return releaseInfo;
47926
+ });
47927
+ }
47928
+ }
47929
+
47930
+ class CheckBLEFirmwareRelease extends BaseMethod {
47931
+ getSupportedProtocols() {
47932
+ return ['V1', 'V2'];
47933
+ }
47934
+ init() {
47935
+ this.allowDeviceMode = [
47936
+ ...this.allowDeviceMode,
47937
+ UI_REQUEST.NOT_INITIALIZE,
47938
+ UI_REQUEST.BOOTLOADER,
47939
+ ];
47940
+ this.useDevicePassphraseState = false;
47941
+ this.skipForceUpdateCheck = true;
47942
+ }
47943
+ run() {
47944
+ return __awaiter(this, void 0, void 0, function* () {
47945
+ if (!this.device.features)
47946
+ return null;
47947
+ if (this.device.isProtocolV2()) {
47948
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47949
+ device: this.device,
47950
+ methodName: 'checkBLEFirmwareRelease',
47951
+ });
47952
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
47953
+ currentVersions: state.versions,
47954
+ firmwareType,
47955
+ release,
47956
+ deviceType: state.identity.deviceType,
47957
+ }), PROTOCOL_V2_BLE_TARGETS);
47958
+ return toProtocolV2FirmwareReleaseInfo({
47959
+ plan,
47960
+ state,
47961
+ release: getProtocolV2ComponentReleaseInfo(plan, 'COPROCESSOR'),
47962
+ });
47963
+ }
47964
+ const releaseInfo = getBleFirmwareReleaseInfo(this.device.features);
47965
+ return releaseInfo;
47966
+ });
47967
+ }
47968
+ }
47969
+
47970
+ class CheckBridgeStatus extends BaseMethod {
47971
+ init() {
47972
+ this.useDevice = false;
47973
+ this.useDevicePassphraseState = false;
47974
+ this.skipForceUpdateCheck = true;
47975
+ }
47976
+ run() {
47977
+ return __awaiter(this, void 0, void 0, function* () {
47978
+ return new Promise((resolve, reject) => {
47979
+ axios__default["default"]
47980
+ .request({
47981
+ url: 'http://localhost:21320',
47982
+ method: 'POST',
47983
+ withCredentials: false,
47984
+ timeout: 3000,
47985
+ })
47986
+ .then(() => resolve(true))
47987
+ .catch(e => {
47988
+ if (e.code === 'ECONNABORTED') {
47989
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BridgeTimeoutError));
47849
47990
  }
47850
- catch (_b) {
47851
- resourceStatus = buildProtocolV2ResourceUpdatePlan({
47852
- resources,
47853
- mode: 'bootloader-recovery',
47854
- }).status;
47991
+ else {
47992
+ resolve(false);
47855
47993
  }
47856
- }
47994
+ });
47995
+ });
47996
+ });
47997
+ }
47998
+ }
47999
+
48000
+ class CheckBridgeRelease extends BaseMethod {
48001
+ init() {
48002
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
48003
+ this.useDevicePassphraseState = false;
48004
+ this.skipForceUpdateCheck = true;
48005
+ }
48006
+ run() {
48007
+ return __awaiter(this, void 0, void 0, function* () {
48008
+ if (!this.device.features) {
48009
+ return null;
47857
48010
  }
47858
- const targetsToUpdate = [
47859
- ...plan.targetsToUpdate,
47860
- ...(resourceStatus === 'outdated' ? ['resource'] : []),
47861
- ];
47862
- const firmwareStatus = plan.status === 'unavailable' ? 'unknown' : plan.status;
47863
- const emptyRelease = 'none';
47864
- return Object.assign(Object.assign({ firmware: {
47865
- status: firmwareStatus,
47866
- changelog: release ? [release.changelog] : [],
47867
- release: release !== null && release !== void 0 ? release : emptyRelease,
47868
- bootloaderMode: state.status.mode === 'bootloader' || state.status.mode === 'romloader',
47869
- }, ble: {
47870
- status: 'valid',
47871
- release: emptyRelease,
47872
- }, bootloader: {
47873
- status: 'valid',
47874
- release: emptyRelease,
47875
- }, features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { resourceStatus, hasUpgrade: plan.hasUpgrade || resourceStatus === 'outdated', targetsToUpdate });
48011
+ const { willUpdateFirmwareVersion } = this.payload;
48012
+ const { features } = this.device;
48013
+ const deviceType = getDeviceType(features);
48014
+ const currentFirmwareVersion = getDeviceFirmwareVersion(features).join('.');
48015
+ return getBridgeReleaseInfo({
48016
+ deviceType,
48017
+ currentFirmwareVersion,
48018
+ willUpdateFirmwareVersion,
48019
+ });
48020
+ });
48021
+ }
48022
+ }
48023
+
48024
+ class CheckBootloaderRelease extends BaseMethod {
48025
+ getSupportedProtocols() {
48026
+ return ['V1', 'V2'];
48027
+ }
48028
+ init() {
48029
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
48030
+ this.useDevicePassphraseState = false;
48031
+ this.skipForceUpdateCheck = true;
48032
+ }
48033
+ run() {
48034
+ var _a;
48035
+ return __awaiter(this, void 0, void 0, function* () {
48036
+ if (!this.device.features)
48037
+ return null;
48038
+ const { features } = this.device;
48039
+ const payload = this.payload;
48040
+ if (this.device.isProtocolV2()) {
48041
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
48042
+ device: this.device,
48043
+ firmwareType: payload.firmwareType,
48044
+ methodName: 'checkBootloaderRelease',
48045
+ });
48046
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
48047
+ currentVersions: state.versions,
48048
+ firmwareType,
48049
+ release,
48050
+ deviceType: state.identity.deviceType,
48051
+ }), PROTOCOL_V2_BOOTLOADER_TARGETS);
48052
+ return toProtocolV2FirmwareReleaseInfo({
48053
+ plan,
48054
+ state,
48055
+ release: getProtocolV2ComponentReleaseInfo(plan, 'BOOTLOADER'),
48056
+ });
48057
+ }
48058
+ const deviceFirmwareType = this.device.getCurrentFirmwareType();
48059
+ const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
48060
+ const releaseInfo = getBootloaderReleaseInfo({
48061
+ features,
48062
+ willUpdateFirmwareVersion: payload.willUpdateFirmwareVersion,
48063
+ firmwareType,
48064
+ });
48065
+ return Promise.resolve(releaseInfo);
47876
48066
  });
47877
48067
  }
47878
48068
  }
@@ -49227,11 +49417,34 @@ class FirmwareUpdateBaseMethod extends BaseMethod {
49227
49417
  let checkCount = 0;
49228
49418
  let hasPromptedWebDevice = false;
49229
49419
  let isPromptingWebDevice = false;
49420
+ let isFinished = false;
49421
+ let intervalTimer;
49230
49422
  let timeoutTimer;
49231
49423
  const isTouchOrProDevice = ((_a = this === null || this === void 0 ? void 0 : this.device) === null || _a === void 0 ? void 0 : _a.getCurrentDeviceType()) === hdShared.EDeviceType.Touch ||
49232
49424
  ((_b = this === null || this === void 0 ? void 0 : this.device) === null || _b === void 0 ? void 0 : _b.getCurrentDeviceType()) === hdShared.EDeviceType.Pro;
49233
- const intervalTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
49425
+ const clearPollingTimers = () => {
49426
+ if (intervalTimer !== undefined) {
49427
+ clearInterval(intervalTimer);
49428
+ intervalTimer = undefined;
49429
+ }
49430
+ if (timeoutTimer !== undefined) {
49431
+ clearTimeout(timeoutTimer);
49432
+ timeoutTimer = undefined;
49433
+ }
49434
+ };
49435
+ const checkForBootloader = (clearActiveTimers) => __awaiter(this, void 0, void 0, function* () {
49436
+ const found = yield this._checkDeviceInBootloaderMode(connectId, clearActiveTimers ? intervalTimer : undefined, clearActiveTimers ? timeoutTimer : undefined);
49437
+ if (found) {
49438
+ isFinished = true;
49439
+ clearPollingTimers();
49440
+ }
49441
+ return found;
49442
+ });
49443
+ let startPolling = () => undefined;
49444
+ const pollForBootloader = () => __awaiter(this, void 0, void 0, function* () {
49234
49445
  var _c, _d, _e, _f;
49446
+ if (isFinished || isPromptingWebDevice)
49447
+ return;
49235
49448
  checkCount += 1;
49236
49449
  Log$a.log('FirmwareUpdateBaseMethod [checkDeviceToBootloader] isFirstCheck: ', isFirstCheck);
49237
49450
  if (isTouchOrProDevice && isFirstCheck) {
@@ -49244,20 +49457,22 @@ class FirmwareUpdateBaseMethod extends BaseMethod {
49244
49457
  !this.payload.skipWebDevicePrompt &&
49245
49458
  !hasPromptedWebDevice &&
49246
49459
  !isPromptingWebDevice) {
49247
- clearInterval(intervalTimer);
49248
- clearTimeout(timeoutTimer);
49460
+ clearPollingTimers();
49249
49461
  isPromptingWebDevice = true;
49250
49462
  try {
49251
49463
  this.postTipMessage(exports.FirmwareUpdateTipMessage.SelectDeviceInBootloaderForWebDevice);
49252
49464
  const confirmed = yield this._promptDeviceInBootloaderForWebDevice();
49253
49465
  hasPromptedWebDevice = true;
49254
49466
  if (confirmed) {
49255
- yield this._checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer);
49467
+ yield checkForBootloader(false);
49468
+ }
49469
+ if (!isFinished) {
49470
+ startPolling();
49256
49471
  }
49257
49472
  }
49258
49473
  catch (e) {
49259
- clearInterval(intervalTimer);
49260
- clearTimeout(timeoutTimer);
49474
+ isFinished = true;
49475
+ clearPollingTimers();
49261
49476
  Log$a.log('FirmwareUpdateBaseMethod [checkDeviceToBootloader] _promptDeviceInBootloaderForWebDevice failed: ', e);
49262
49477
  (_c = this.checkPromise) === null || _c === void 0 ? void 0 : _c.reject(e);
49263
49478
  }
@@ -49274,7 +49489,8 @@ class FirmwareUpdateBaseMethod extends BaseMethod {
49274
49489
  yield ((_d = this.device.deviceConnector) === null || _d === void 0 ? void 0 : _d.acquire(this.device.originalDescriptor.id, null, true, (_e = this.payload.connectProtocol) !== null && _e !== void 0 ? _e : this.device.originalDescriptor.protocolType));
49275
49490
  yield this.device.initialize({ timeoutMs: BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS });
49276
49491
  if (this.device.isBootloader()) {
49277
- clearInterval(intervalTimer);
49492
+ isFinished = true;
49493
+ clearPollingTimers();
49278
49494
  (_f = this.checkPromise) === null || _f === void 0 ? void 0 : _f.resolve(true);
49279
49495
  }
49280
49496
  }
@@ -49286,15 +49502,32 @@ class FirmwareUpdateBaseMethod extends BaseMethod {
49286
49502
  }
49287
49503
  }
49288
49504
  else {
49289
- yield this._checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer);
49505
+ yield checkForBootloader(true);
49290
49506
  }
49291
- }), isBleReconnect ? 3000 : 2000);
49292
- timeoutTimer = setTimeout(() => {
49293
- if (this.checkPromise) {
49294
- clearInterval(intervalTimer);
49295
- this.checkPromise.reject(new Error());
49296
- }
49297
- }, 30000);
49507
+ });
49508
+ startPolling = () => {
49509
+ if (isFinished)
49510
+ return;
49511
+ clearPollingTimers();
49512
+ intervalTimer = setInterval(() => {
49513
+ pollForBootloader().catch(error => {
49514
+ var _a;
49515
+ if (isFinished)
49516
+ return;
49517
+ isFinished = true;
49518
+ clearPollingTimers();
49519
+ (_a = this.checkPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
49520
+ });
49521
+ }, isBleReconnect ? 3000 : 2000);
49522
+ timeoutTimer = setTimeout(() => {
49523
+ if (!isFinished && this.checkPromise) {
49524
+ isFinished = true;
49525
+ clearPollingTimers();
49526
+ this.checkPromise.reject(new Error());
49527
+ }
49528
+ }, 30000);
49529
+ };
49530
+ startPolling();
49298
49531
  }
49299
49532
  _checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer) {
49300
49533
  var _a, _b, _c, _d, _e;
@@ -49619,35 +49852,52 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49619
49852
  });
49620
49853
  }
49621
49854
  run() {
49622
- var _a, _b, _c;
49855
+ var _a, _b;
49623
49856
  return __awaiter(this, void 0, void 0, function* () {
49624
49857
  const { device } = this;
49625
49858
  const { features } = device;
49626
49859
  const payload = this.payload;
49627
- const hostBinding = payload.artifact
49628
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
49860
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
49861
+ if (hasPreparedPlan && payload.binary !== undefined) {
49862
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared bootloader plans cannot be combined with a legacy binary');
49863
+ }
49864
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
49865
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Bootloader artifacts require a prepared plan');
49866
+ }
49867
+ const preparedPlan = hasPreparedPlan
49868
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
49629
49869
  : undefined;
49630
- const executionParams = Object.assign(Object.assign({}, payload), { artifactReader: (_a = hostBinding === null || hostBinding === void 0 ? void 0 : hostBinding.artifactReader) !== null && _a !== void 0 ? _a : payload.artifactReader });
49631
- if (payload.artifact) {
49870
+ const plannedArtifact = preparedPlan
49871
+ ? getFirmwareUpdatePreparedRawArtifact({
49872
+ preparedPlan,
49873
+ target: 'bootloader',
49874
+ role: 'bootloader',
49875
+ }).artifact
49876
+ : undefined;
49877
+ const artifactReader = preparedPlan
49878
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
49879
+ : payload.artifactReader;
49880
+ const executionParams = Object.assign(Object.assign({}, payload), { artifactReader });
49881
+ if (preparedPlan && plannedArtifact) {
49632
49882
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
49633
- preparedPlan: payload.preparedPlan,
49883
+ preparedPlan,
49634
49884
  deviceIdentity: getDeviceUUID(features) || undefined,
49635
49885
  bootloaderMode: resolveDeviceBootloaderMode(features),
49636
49886
  deviceModel: String(getDeviceType(features)),
49637
49887
  });
49638
49888
  assertFirmwareUpdatePreparedPlanBinding({
49639
- preparedPlan: payload.preparedPlan,
49889
+ preparedPlan,
49640
49890
  executor: 'v2',
49641
49891
  scopeTargets: ['bootloader'],
49642
49892
  bindings: [
49643
49893
  {
49644
49894
  target: 'bootloader',
49645
- artifact: payload.artifact,
49895
+ artifact: (_a = payload.artifact) !== null && _a !== void 0 ? _a : plannedArtifact,
49646
49896
  },
49647
49897
  ],
49648
49898
  });
49649
49899
  const source = yield openFirmwareByteSource({
49650
- artifact: payload.artifact,
49900
+ artifact: plannedArtifact,
49651
49901
  reader: executionParams.artifactReader,
49652
49902
  });
49653
49903
  if (!source) {
@@ -49655,13 +49905,11 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49655
49905
  }
49656
49906
  try {
49657
49907
  const deviceType = device.getCurrentDeviceType();
49658
- const deviceFirmwareType = device.getCurrentFirmwareType();
49659
- const firmwareType = (_b = payload.firmwareType) !== null && _b !== void 0 ? _b : deviceFirmwareType;
49660
49908
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
49661
49909
  return yield this.updateTouchBootloader({
49662
49910
  device,
49663
49911
  features,
49664
- firmwareType,
49912
+ firmwareType: preparedPlan.firmwareType,
49665
49913
  source,
49666
49914
  });
49667
49915
  }
@@ -49674,7 +49922,7 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49674
49922
  const { binary } = payload;
49675
49923
  const deviceType = device.getCurrentDeviceType();
49676
49924
  const deviceFirmwareType = device.getCurrentFirmwareType();
49677
- const firmwareType = (_c = payload.firmwareType) !== null && _c !== void 0 ? _c : deviceFirmwareType;
49925
+ const firmwareType = (_b = payload.firmwareType) !== null && _b !== void 0 ? _b : deviceFirmwareType;
49678
49926
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
49679
49927
  return this.updateTouchBootloader({
49680
49928
  device,
@@ -49935,6 +50183,119 @@ const normalizeFirmwarePreparationError = (error) => {
49935
50183
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, error instanceof Error ? error.message : String(error));
49936
50184
  };
49937
50185
 
50186
+ const PREPARED_RESOURCE_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024;
50187
+ const PREPARED_RESOURCE_ENTRY_MAX_COUNT = 512;
50188
+ const resourceArchiveError = (message, firmwareUpdateCode) => {
50189
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, message, { firmwareUpdateCode });
50190
+ };
50191
+ const getZipEntrySizes = (entry) => {
50192
+ var _a;
50193
+ const { compressedSize, uncompressedSize } = (_a = entry._data) !== null && _a !== void 0 ? _a : {};
50194
+ if (!Number.isSafeInteger(compressedSize) ||
50195
+ Number(compressedSize) < 0 ||
50196
+ !Number.isSafeInteger(uncompressedSize) ||
50197
+ Number(uncompressedSize) <= 0) {
50198
+ return resourceArchiveError(`Firmware prepared resource ZIP entry size is invalid: ${entry.name}`, 'FirmwareArtifactsNotPrepared');
50199
+ }
50200
+ return {
50201
+ compressedSize: Number(compressedSize),
50202
+ uncompressedSize: Number(uncompressedSize),
50203
+ };
50204
+ };
50205
+ const readVerifiedPreparedResourceArchive = ({ preparedPlan, reader, }) => __awaiter(void 0, void 0, void 0, function* () {
50206
+ const resourceArtifacts = preparedPlan.artifacts.filter(artifact => artifact.target === 'resource');
50207
+ if (!preparedPlan.targetsToUpdate.includes('resource')) {
50208
+ if (resourceArtifacts.length > 0) {
50209
+ return resourceArchiveError('Firmware prepared resource artifact is outside the approved targets', 'FirmwareArtifactsNotPrepared');
50210
+ }
50211
+ return [];
50212
+ }
50213
+ if (resourceArtifacts.length !== 1) {
50214
+ return resourceArchiveError('Firmware prepared plan must contain exactly one materialized resource ZIP', 'FirmwareArtifactsNotPrepared');
50215
+ }
50216
+ const archiveArtifact = resourceArtifacts[0];
50217
+ const preparedEntries = archiveArtifact.materializedEntries;
50218
+ if (archiveArtifact.role !== 'resource' ||
50219
+ archiveArtifact.container !== 'zip' ||
50220
+ !(preparedEntries === null || preparedEntries === void 0 ? void 0 : preparedEntries.length)) {
50221
+ return resourceArchiveError('Firmware prepared plan must contain exactly one materialized resource ZIP', 'FirmwareArtifactsNotPrepared');
50222
+ }
50223
+ const archiveSource = yield openFirmwareByteSource({
50224
+ artifact: archiveArtifact.artifact,
50225
+ reader,
50226
+ });
50227
+ if (!archiveSource) {
50228
+ return resourceArchiveError('Firmware prepared resource ZIP is unavailable', 'FirmwareArtifactsNotPrepared');
50229
+ }
50230
+ if (archiveSource.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
50231
+ yield archiveSource.close().catch(() => undefined);
50232
+ return resourceArchiveError('Firmware prepared resource ZIP exceeds the archive size limit', 'FirmwareArtifactsNotPrepared');
50233
+ }
50234
+ let archiveBinary;
50235
+ try {
50236
+ archiveBinary = yield readFirmwareByteSourceFully(archiveSource);
50237
+ }
50238
+ finally {
50239
+ yield archiveSource.close().catch(() => undefined);
50240
+ }
50241
+ const archiveDigest = utils.bytesToHex(sha256.sha256(new Uint8Array(archiveBinary)));
50242
+ if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
50243
+ return resourceArchiveError('Firmware prepared resource ZIP does not match its approved receipt', 'FirmwareArtifactReceiptMismatch');
50244
+ }
50245
+ let zip;
50246
+ try {
50247
+ zip = yield JSZip__default["default"].loadAsync(archiveBinary);
50248
+ }
50249
+ catch (_a) {
50250
+ return resourceArchiveError('Firmware prepared resource ZIP cannot be parsed', 'FirmwareArtifactsNotPrepared');
50251
+ }
50252
+ const zipEntries = Object.values(zip.files);
50253
+ if (zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)) {
50254
+ return resourceArchiveError('Firmware prepared resource ZIP contains an unsafe entry path', 'FirmwareArtifactsNotPrepared');
50255
+ }
50256
+ const files = zipEntries.filter(entry => !entry.dir);
50257
+ if (files.length === 0 || files.length > PREPARED_RESOURCE_ENTRY_MAX_COUNT) {
50258
+ return resourceArchiveError('Firmware prepared resource ZIP entry set is invalid', 'FirmwareArtifactsNotPrepared');
50259
+ }
50260
+ let totalSize = 0;
50261
+ const canonicalNames = new Set();
50262
+ for (const entry of files) {
50263
+ const { compressedSize, uncompressedSize } = getZipEntrySizes(entry);
50264
+ const resourceName = getFirmwareUpdateResourceName(entry.name);
50265
+ const canonicalName = resourceName.toLowerCase();
50266
+ totalSize += uncompressedSize;
50267
+ if (compressedSize > archiveBinary.byteLength ||
50268
+ uncompressedSize > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES ||
50269
+ totalSize > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES ||
50270
+ canonicalNames.has(canonicalName)) {
50271
+ return resourceArchiveError(`Firmware prepared resource ZIP entry bounds are invalid: ${entry.name}`, 'FirmwareArtifactsNotPrepared');
50272
+ }
50273
+ canonicalNames.add(canonicalName);
50274
+ }
50275
+ const preparedEntriesByName = new Map(preparedEntries.map(entry => [entry.entryName, entry]));
50276
+ if (preparedEntriesByName.size !== files.length || preparedEntries.length !== files.length) {
50277
+ return resourceArchiveError('Firmware prepared resource entries do not match the approved ZIP', 'FirmwareArtifactReceiptMismatch');
50278
+ }
50279
+ const verifiedEntries = [];
50280
+ for (const entry of files) {
50281
+ const { uncompressedSize } = getZipEntrySizes(entry);
50282
+ const binary = yield entry.async('arraybuffer');
50283
+ const preparedEntry = preparedEntriesByName.get(entry.name);
50284
+ const digest = utils.bytesToHex(sha256.sha256(new Uint8Array(binary)));
50285
+ if (binary.byteLength !== uncompressedSize ||
50286
+ !preparedEntry ||
50287
+ preparedEntry.artifact.size !== binary.byteLength ||
50288
+ preparedEntry.artifact.sha256.toLowerCase() !== digest) {
50289
+ return resourceArchiveError(`Firmware prepared resource entry does not match the approved ZIP: ${entry.name}`, 'FirmwareArtifactReceiptMismatch');
50290
+ }
50291
+ verifiedEntries.push({
50292
+ entryName: getFirmwareUpdateResourceName(entry.name),
50293
+ binary,
50294
+ });
50295
+ }
50296
+ return verifiedEntries;
50297
+ });
50298
+
49938
50299
  const Log$8 = getLogger(exports.LoggerNames.Method);
49939
50300
  const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
49940
50301
  connectTimeoutMs: 60000,
@@ -50009,7 +50370,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50009
50370
  };
50010
50371
  }
50011
50372
  init() {
50012
- var _a;
50373
+ var _a, _b;
50013
50374
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50014
50375
  this.requireDeviceMode = [];
50015
50376
  this.useDevicePassphraseState = false;
@@ -50022,11 +50383,12 @@ class FirmwareUpdateV2 extends BaseMethod {
50022
50383
  { name: 'platform', type: 'string', required: true },
50023
50384
  { name: 'firmwareType', type: 'string' },
50024
50385
  ]);
50025
- if ('binary' in payload &&
50026
- payload.binary !== undefined &&
50027
- 'artifact' in payload &&
50028
- payload.artifact !== undefined) {
50029
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware update binary and artifact are mutually exclusive');
50386
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
50387
+ if (hasPreparedPlan && (payload.binary !== undefined || payload.version !== undefined)) {
50388
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy firmware inputs');
50389
+ }
50390
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
50391
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware artifacts require a prepared plan');
50030
50392
  }
50031
50393
  if (!payload.updateType) {
50032
50394
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'updateType is required');
@@ -50036,28 +50398,37 @@ class FirmwareUpdateV2 extends BaseMethod {
50036
50398
  forcedUpdateRes: payload.forcedUpdateRes,
50037
50399
  isUpdateBootloader: payload.isUpdateBootloader,
50038
50400
  };
50039
- if ('version' in payload) {
50401
+ if (!hasPreparedPlan && 'version' in payload) {
50040
50402
  this.params = Object.assign(Object.assign({}, this.params), { version: payload.version, firmwareType: payload.firmwareType });
50041
50403
  }
50042
- if ('binary' in payload) {
50404
+ if (!hasPreparedPlan && 'binary' in payload) {
50043
50405
  this.params = Object.assign(Object.assign({}, this.params), { binary: payload.binary });
50044
50406
  }
50045
- if ('artifact' in payload) {
50046
- const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration);
50407
+ if (hasPreparedPlan) {
50408
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(payload.preparedPlan);
50409
+ const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest);
50047
50410
  const target = payload.isUpdateBootloader ? 'bootloader' : payload.updateType;
50411
+ const plannedArtifact = getFirmwareUpdatePreparedRawArtifact({
50412
+ preparedPlan,
50413
+ target,
50414
+ role: target,
50415
+ }).artifact;
50048
50416
  const resourceBindings = ((_a = payload.resourceEntries) !== null && _a !== void 0 ? _a : []).map((entry) => ({
50049
50417
  target: 'resource',
50050
50418
  entryName: entry.entryName,
50051
50419
  artifact: entry.artifact,
50052
50420
  }));
50053
50421
  assertFirmwareUpdatePreparedPlanBinding({
50054
- preparedPlan: payload.preparedPlan,
50422
+ preparedPlan,
50055
50423
  executor: 'v2',
50056
50424
  platform: payload.platform,
50057
- scopeTargets: [target, ...(target === 'firmware' ? ['resource'] : [])],
50058
- bindings: [{ target, artifact: payload.artifact }, ...resourceBindings],
50425
+ scopeTargets: [
50426
+ target,
50427
+ ...(target === 'firmware' && resourceBindings.length ? ['resource'] : []),
50428
+ ],
50429
+ bindings: [{ target, artifact: (_b = payload.artifact) !== null && _b !== void 0 ? _b : plannedArtifact }, ...resourceBindings],
50059
50430
  });
50060
- this.params = Object.assign(Object.assign({}, this.params), { preparedPlan: payload.preparedPlan, artifact: payload.artifact, resourceEntries: payload.resourceEntries, artifactReader: hostBinding.artifactReader });
50431
+ this.params = Object.assign(Object.assign({}, this.params), { preparedPlan, artifact: plannedArtifact, artifactReader: hostBinding.artifactReader, firmwareType: preparedPlan.firmwareType });
50061
50432
  }
50062
50433
  }
50063
50434
  _promptDeviceInBootloaderForWebDevice() {
@@ -50085,11 +50456,36 @@ class FirmwareUpdateV2 extends BaseMethod {
50085
50456
  Log$8.log('FirmwareUpdateV2 [checkDeviceToBootloader] isBleReconnect: ', isBleReconnect);
50086
50457
  let isFirstCheck = true;
50087
50458
  let checkCount = 0;
50459
+ let hasPromptedWebDevice = false;
50460
+ let isPromptingWebDevice = false;
50461
+ let isFinished = false;
50462
+ let intervalTimer;
50088
50463
  let timeoutTimer;
50089
50464
  const deviceType = (_a = this.device) === null || _a === void 0 ? void 0 : _a.getCurrentDeviceType();
50090
50465
  const isTouchOrProDevice = deviceType === hdShared.EDeviceType.Touch || deviceType === hdShared.EDeviceType.Pro;
50091
- const intervalTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
50466
+ const clearPollingTimers = () => {
50467
+ if (intervalTimer !== undefined) {
50468
+ clearInterval(intervalTimer);
50469
+ intervalTimer = undefined;
50470
+ }
50471
+ if (timeoutTimer !== undefined) {
50472
+ clearTimeout(timeoutTimer);
50473
+ timeoutTimer = undefined;
50474
+ }
50475
+ };
50476
+ const checkForBootloader = (clearActiveTimers) => __awaiter(this, void 0, void 0, function* () {
50477
+ const found = yield this._checkDeviceInBootloaderMode(connectId, clearActiveTimers ? intervalTimer : undefined, clearActiveTimers ? timeoutTimer : undefined);
50478
+ if (found) {
50479
+ isFinished = true;
50480
+ clearPollingTimers();
50481
+ }
50482
+ return found;
50483
+ });
50484
+ let startPolling = () => undefined;
50485
+ const pollForBootloader = () => __awaiter(this, void 0, void 0, function* () {
50092
50486
  var _b, _c, _d, _e;
50487
+ if (isFinished || isPromptingWebDevice)
50488
+ return;
50093
50489
  checkCount += 1;
50094
50490
  Log$8.log('FirmwareUpdateV2 [checkDeviceToBootloader] isFirstCheck: ', isFirstCheck);
50095
50491
  if (isTouchOrProDevice && isFirstCheck) {
@@ -50099,20 +50495,31 @@ class FirmwareUpdateV2 extends BaseMethod {
50099
50495
  }
50100
50496
  if (checkCount > 4 &&
50101
50497
  DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
50102
- !this.payload.skipWebDevicePrompt) {
50103
- clearInterval(intervalTimer);
50104
- clearTimeout(timeoutTimer);
50498
+ !this.payload.skipWebDevicePrompt &&
50499
+ !hasPromptedWebDevice &&
50500
+ !isPromptingWebDevice) {
50501
+ clearPollingTimers();
50502
+ isPromptingWebDevice = true;
50105
50503
  try {
50106
50504
  this.postTipMessage(exports.FirmwareUpdateTipMessage.SelectDeviceInBootloaderForWebDevice);
50107
50505
  const confirmed = yield this._promptDeviceInBootloaderForWebDevice();
50506
+ hasPromptedWebDevice = true;
50108
50507
  if (confirmed) {
50109
- yield this._checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer);
50508
+ yield checkForBootloader(false);
50509
+ }
50510
+ if (!isFinished) {
50511
+ startPolling();
50110
50512
  }
50111
50513
  }
50112
50514
  catch (e) {
50515
+ isFinished = true;
50516
+ clearPollingTimers();
50113
50517
  Log$8.log('FirmwareUpdateV2 [checkDeviceToBootloader] promptDeviceInBootloaderForWebDevice failed: ', e);
50114
50518
  (_b = this.checkPromise) === null || _b === void 0 ? void 0 : _b.reject(e);
50115
50519
  }
50520
+ finally {
50521
+ isPromptingWebDevice = false;
50522
+ }
50116
50523
  return;
50117
50524
  }
50118
50525
  if (isBleReconnect) {
@@ -50123,7 +50530,8 @@ class FirmwareUpdateV2 extends BaseMethod {
50123
50530
  yield ((_c = this.device.deviceConnector) === null || _c === void 0 ? void 0 : _c.acquire(this.device.originalDescriptor.id, null, true, (_d = this.payload.connectProtocol) !== null && _d !== void 0 ? _d : this.device.originalDescriptor.protocolType));
50124
50531
  yield this.device.initialize({ timeoutMs: BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS });
50125
50532
  if (this.device.isBootloader()) {
50126
- clearInterval(intervalTimer);
50533
+ isFinished = true;
50534
+ clearPollingTimers();
50127
50535
  (_e = this.checkPromise) === null || _e === void 0 ? void 0 : _e.resolve(true);
50128
50536
  }
50129
50537
  }
@@ -50135,15 +50543,32 @@ class FirmwareUpdateV2 extends BaseMethod {
50135
50543
  }
50136
50544
  }
50137
50545
  else {
50138
- yield this._checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer);
50546
+ yield checkForBootloader(true);
50139
50547
  }
50140
- }), isBleReconnect ? 3000 : 2000);
50141
- timeoutTimer = setTimeout(() => {
50142
- if (this.checkPromise) {
50143
- clearInterval(intervalTimer);
50144
- this.checkPromise.reject(new Error());
50145
- }
50146
- }, 30000);
50548
+ });
50549
+ startPolling = () => {
50550
+ if (isFinished)
50551
+ return;
50552
+ clearPollingTimers();
50553
+ intervalTimer = setInterval(() => {
50554
+ pollForBootloader().catch(error => {
50555
+ var _a;
50556
+ if (isFinished)
50557
+ return;
50558
+ isFinished = true;
50559
+ clearPollingTimers();
50560
+ (_a = this.checkPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
50561
+ });
50562
+ }, isBleReconnect ? 3000 : 2000);
50563
+ timeoutTimer = setTimeout(() => {
50564
+ if (!isFinished && this.checkPromise) {
50565
+ isFinished = true;
50566
+ clearPollingTimers();
50567
+ this.checkPromise.reject(new Error());
50568
+ }
50569
+ }, 30000);
50570
+ };
50571
+ startPolling();
50147
50572
  }
50148
50573
  _checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer) {
50149
50574
  var _a, _b, _c, _d, _e;
@@ -50207,7 +50632,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50207
50632
  }
50208
50633
  }
50209
50634
  run() {
50210
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
50635
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
50211
50636
  return __awaiter(this, void 0, void 0, function* () {
50212
50637
  const { device, params } = this;
50213
50638
  const { features, commands } = device;
@@ -50295,13 +50720,48 @@ class FirmwareUpdateV2 extends BaseMethod {
50295
50720
  throw normalizeFirmwarePreparationError(err);
50296
50721
  }
50297
50722
  });
50723
+ const preparedResourceRequested = !params.isUpdateBootloader &&
50724
+ params.updateType === 'firmware' &&
50725
+ ((_c = (_b = params.preparedPlan) === null || _b === void 0 ? void 0 : _b.targetsToUpdate.includes('resource')) !== null && _c !== void 0 ? _c : false);
50726
+ if (preparedResourceRequested && device.isBootloader()) {
50727
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware resources require the device application mode', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared', artifactName: 'resource' });
50728
+ }
50298
50729
  if (!device.isBootloader() && features) {
50299
50730
  const serialNo = device.getCurrentSerialNo();
50300
50731
  if (this.isEnteredManuallyBoot()) {
50301
50732
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateManuallyEnterBoot);
50302
50733
  }
50303
50734
  yield acquireFirmwareSource();
50304
- if (this.isSupportResourceUpdate(params.updateType)) {
50735
+ if (preparedResourceRequested) {
50736
+ if (!this.isSupportResourceUpdate(params.updateType)) {
50737
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware resources are not supported by this device', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared', artifactName: 'resource' });
50738
+ }
50739
+ this.postTipMessage('CheckLatestUiResource');
50740
+ this.postTipMessage('DownloadLatestUiResource');
50741
+ const verifiedEntries = yield readVerifiedPreparedResourceArchive({
50742
+ preparedPlan: params.preparedPlan,
50743
+ reader: params.artifactReader,
50744
+ });
50745
+ const sources = [];
50746
+ try {
50747
+ for (const entry of verifiedEntries) {
50748
+ const source = yield openFirmwareByteSource({ binary: entry.binary });
50749
+ if (!source) {
50750
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is empty`, {
50751
+ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
50752
+ artifactName: 'resource',
50753
+ });
50754
+ }
50755
+ sources.push({ entryName: entry.entryName, source });
50756
+ }
50757
+ yield updateResourcesFromSources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, sources);
50758
+ }
50759
+ finally {
50760
+ yield Promise.all(sources.map(entry => entry.source.close().catch(() => undefined)));
50761
+ }
50762
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
50763
+ }
50764
+ else if (this.isSupportResourceUpdate(params.updateType)) {
50305
50765
  this.postTipMessage('CheckLatestUiResource');
50306
50766
  const resourceUrl = DataManager.getSysResourcesLatestRelease({
50307
50767
  features,
@@ -50310,46 +50770,20 @@ class FirmwareUpdateV2 extends BaseMethod {
50310
50770
  });
50311
50771
  if (resourceUrl) {
50312
50772
  this.postTipMessage('DownloadLatestUiResource');
50313
- if ((_b = params.resourceEntries) === null || _b === void 0 ? void 0 : _b.length) {
50314
- const sources = [];
50315
- try {
50316
- for (const entry of params.resourceEntries) {
50317
- const resourceName = getFirmwareUpdateResourceName(entry.entryName);
50318
- const source = yield openFirmwareByteSource({
50319
- artifact: entry.artifact,
50320
- reader: params.artifactReader,
50321
- });
50322
- if (!source) {
50323
- throw new Error('Firmware resource entry is not prepared');
50324
- }
50325
- sources.push({ entryName: resourceName, source });
50326
- }
50327
- yield updateResourcesFromSources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, sources.map(entry => ({
50328
- entryName: entry.entryName,
50329
- source: entry.source,
50330
- })));
50331
- }
50332
- finally {
50333
- yield Promise.all(sources.map(entry => entry.source.close().catch(() => undefined)));
50334
- }
50335
- this.postTipMessage('DownloadLatestUiResourceSuccess');
50336
- }
50337
- else {
50338
- if (params.artifactReader ||
50339
- DataManager.getSettings('firmwareManifestMode') === 'external-only') {
50340
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource must be prepared by the external firmware host', {
50341
- firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
50342
- artifactName: 'resource',
50343
- });
50344
- }
50345
- const resourceBinary = (yield getSysResourceBinary(resourceUrl))
50346
- .binary;
50347
- this.postTipMessage('DownloadLatestUiResourceSuccess');
50348
- yield updateResources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, resourceBinary);
50773
+ if (params.artifactReader ||
50774
+ DataManager.getSettings('firmwareManifestMode') === 'external-only') {
50775
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource must be prepared by the external firmware host', {
50776
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
50777
+ artifactName: 'resource',
50778
+ });
50349
50779
  }
50780
+ const resourceBinary = (yield getSysResourceBinary(resourceUrl))
50781
+ .binary;
50782
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
50783
+ yield updateResources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, resourceBinary);
50350
50784
  }
50351
50785
  }
50352
- (_d = (_c = this.device) === null || _c === void 0 ? void 0 : _c.commands) === null || _d === void 0 ? void 0 : _d.checkDisposed();
50786
+ (_e = (_d = this.device) === null || _d === void 0 ? void 0 : _d.commands) === null || _e === void 0 ? void 0 : _e.checkDisposed();
50353
50787
  try {
50354
50788
  this.postTipMessage('AutoRebootToBootloader');
50355
50789
  const bootRes = yield commands.typedCall('DeviceBackToBoot', 'Success');
@@ -50362,9 +50796,9 @@ class FirmwareUpdateV2 extends BaseMethod {
50362
50796
  DevicePool.clearDeviceCache(serialNo);
50363
50797
  }
50364
50798
  delete DevicePool.devicesCache[''];
50365
- yield ((_e = this.checkPromise) === null || _e === void 0 ? void 0 : _e.promise);
50799
+ yield ((_f = this.checkPromise) === null || _f === void 0 ? void 0 : _f.promise);
50366
50800
  this.checkPromise = null;
50367
- (_g = (_f = this.device) === null || _f === void 0 ? void 0 : _f.commands) === null || _g === void 0 ? void 0 : _g.checkDisposed();
50801
+ (_h = (_g = this.device) === null || _g === void 0 ? void 0 : _g.commands) === null || _h === void 0 ? void 0 : _h.checkDisposed();
50368
50802
  const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
50369
50803
  yield wait(isTouch ? 3000 : 1500);
50370
50804
  }
@@ -50377,7 +50811,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50377
50811
  }
50378
50812
  }
50379
50813
  const source = yield acquireFirmwareSource();
50380
- (_j = (_h = this.device) === null || _h === void 0 ? void 0 : _h.commands) === null || _j === void 0 ? void 0 : _j.checkDisposed();
50814
+ (_k = (_j = this.device) === null || _j === void 0 ? void 0 : _j.commands) === null || _k === void 0 ? void 0 : _k.checkDisposed();
50381
50815
  yield this.device.acquire();
50382
50816
  const response = yield uploadFirmwareFromSource(params.updateType, this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, source, true, params.isUpdateBootloader);
50383
50817
  if (this.connectId) {
@@ -50402,7 +50836,7 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50402
50836
  this.artifactSources = [];
50403
50837
  }
50404
50838
  init() {
50405
- var _a, _b, _c, _d, _e, _f;
50839
+ var _a, _b, _c;
50406
50840
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50407
50841
  this.requireDeviceMode = [];
50408
50842
  this.useDevicePassphraseState = false;
@@ -50420,50 +50854,80 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50420
50854
  { name: 'firmwareType', type: 'string' },
50421
50855
  { name: 'platform', type: 'string' },
50422
50856
  ]);
50423
- const hostBinding = payload.artifacts || payload.preparedPlan
50424
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
50857
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
50858
+ if (!hasPreparedPlan && payload.artifacts !== undefined) {
50859
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware artifacts require a prepared plan');
50860
+ }
50861
+ const preparedPlan = hasPreparedPlan
50862
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
50425
50863
  : undefined;
50426
- if (hostBinding) {
50864
+ const hasLegacyInputs = [
50865
+ payload.bleVersion,
50866
+ payload.bleBinary,
50867
+ payload.firmwareVersion,
50868
+ payload.firmwareBinary,
50869
+ payload.resourceBinary,
50870
+ payload.bootloaderVersion,
50871
+ payload.bootloaderBinary,
50872
+ ].some(input => input !== undefined);
50873
+ if (preparedPlan && hasLegacyInputs) {
50874
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy firmware inputs');
50875
+ }
50876
+ const artifactReader = preparedPlan
50877
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
50878
+ : payload.artifactReader;
50879
+ const preparedArtifacts = {};
50880
+ if (preparedPlan) {
50881
+ const componentTargets = ['bootloader', 'firmware', 'ble'];
50882
+ const supportedTargets = new Set([...componentTargets, 'resource']);
50883
+ const unsupportedTarget = preparedPlan.targetsToUpdate.find(target => !supportedTargets.has(target));
50884
+ if (unsupportedTarget) {
50885
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware prepared plan target ${unsupportedTarget} is not supported by V3`, { firmwareUpdateCode: 'FirmwarePreparedPlanInvalid' });
50886
+ }
50887
+ for (const target of componentTargets) {
50888
+ if (preparedPlan.targetsToUpdate.includes(target)) {
50889
+ preparedArtifacts[target] = getFirmwareUpdatePreparedRawArtifact({
50890
+ preparedPlan,
50891
+ target,
50892
+ role: target,
50893
+ }).artifact;
50894
+ }
50895
+ }
50896
+ const resourceBindings = ((_b = (_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a.resourceEntries) !== null && _b !== void 0 ? _b : []).map((entry) => ({
50897
+ target: 'resource',
50898
+ entryName: entry.entryName,
50899
+ artifact: entry.artifact,
50900
+ }));
50427
50901
  assertFirmwareUpdatePreparedPlanBinding({
50428
- preparedPlan: payload.preparedPlan,
50902
+ preparedPlan,
50429
50903
  executor: 'v3',
50430
50904
  platform: payload.platform,
50431
- scopeTargets: ['bootloader', 'firmware', 'resource', 'ble'],
50905
+ scopeTargets: [
50906
+ 'bootloader',
50907
+ 'firmware',
50908
+ 'ble',
50909
+ ...(resourceBindings.length ? ['resource'] : []),
50910
+ ],
50432
50911
  bindings: [
50433
- ...(((_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a.bootloader)
50434
- ? [
50435
- {
50436
- target: 'bootloader',
50437
- artifact: payload.artifacts.bootloader,
50438
- },
50439
- ]
50440
- : []),
50441
- ...(((_b = payload.artifacts) === null || _b === void 0 ? void 0 : _b.firmware)
50442
- ? [
50443
- {
50444
- target: 'firmware',
50445
- artifact: payload.artifacts.firmware,
50446
- },
50447
- ]
50448
- : []),
50449
- ...(((_c = payload.artifacts) === null || _c === void 0 ? void 0 : _c.ble)
50450
- ? [
50451
- {
50452
- target: 'ble',
50453
- artifact: payload.artifacts.ble,
50454
- },
50455
- ]
50456
- : []),
50457
- ...((_e = (_d = payload.artifacts) === null || _d === void 0 ? void 0 : _d.resourceEntries) !== null && _e !== void 0 ? _e : []).map((entry) => ({
50458
- target: 'resource',
50459
- entryName: entry.entryName,
50460
- artifact: entry.artifact,
50461
- })),
50912
+ ...componentTargets.flatMap(target => {
50913
+ var _a;
50914
+ const plannedArtifact = preparedArtifacts[target];
50915
+ const suppliedArtifact = (_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a[target];
50916
+ return plannedArtifact || suppliedArtifact
50917
+ ? [
50918
+ {
50919
+ target,
50920
+ artifact: suppliedArtifact !== null && suppliedArtifact !== void 0 ? suppliedArtifact : plannedArtifact,
50921
+ },
50922
+ ]
50923
+ : [];
50924
+ }),
50925
+ ...resourceBindings,
50462
50926
  ],
50463
50927
  });
50464
50928
  }
50465
50929
  this.params = {
50466
- preparedPlan: payload.preparedPlan,
50930
+ preparedPlan,
50467
50931
  bleBinary: payload.bleBinary,
50468
50932
  firmwareBinary: payload.firmwareBinary,
50469
50933
  forcedUpdateRes: payload.forcedUpdateRes,
@@ -50472,10 +50936,10 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50472
50936
  bootloaderBinary: payload.bootloaderBinary,
50473
50937
  firmwareVersion: payload.firmwareVersion,
50474
50938
  resourceBinary: payload.resourceBinary,
50475
- firmwareType: payload.firmwareType,
50939
+ firmwareType: (_c = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _c !== void 0 ? _c : payload.firmwareType,
50476
50940
  platform: payload.platform,
50477
- artifactReader: (_f = hostBinding === null || hostBinding === void 0 ? void 0 : hostBinding.artifactReader) !== null && _f !== void 0 ? _f : payload.artifactReader,
50478
- artifacts: payload.artifacts,
50941
+ artifactReader,
50942
+ artifacts: preparedPlan ? preparedArtifacts : undefined,
50479
50943
  };
50480
50944
  }
50481
50945
  run() {
@@ -50581,19 +51045,21 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50581
51045
  prepareResourceInput(firmwareType) {
50582
51046
  var _a;
50583
51047
  return __awaiter(this, void 0, void 0, function* () {
50584
- const preparedEntries = (_a = this.params.artifacts) === null || _a === void 0 ? void 0 : _a.resourceEntries;
50585
- if ((preparedEntries === null || preparedEntries === void 0 ? void 0 : preparedEntries.length) && this.params.resourceBinary) {
50586
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource input must not contain both archive binary and prepared entries');
50587
- }
50588
- if (preparedEntries === null || preparedEntries === void 0 ? void 0 : preparedEntries.length) {
51048
+ if ((_a = this.params.preparedPlan) === null || _a === void 0 ? void 0 : _a.targetsToUpdate.includes('resource')) {
51049
+ const verifiedEntries = yield readVerifiedPreparedResourceArchive({
51050
+ preparedPlan: this.params.preparedPlan,
51051
+ reader: this.params.artifactReader,
51052
+ });
50589
51053
  const resourceEntries = [];
50590
- for (const entry of preparedEntries) {
50591
- const resourceName = getFirmwareUpdateResourceName(entry.entryName);
50592
- const source = yield this.openArtifactSource(undefined, entry.artifact);
51054
+ for (const entry of verifiedEntries) {
51055
+ const source = yield this.openArtifactSource(entry.binary, undefined);
50593
51056
  if (!source) {
50594
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is not prepared`);
51057
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is empty`, {
51058
+ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
51059
+ artifactName: 'resource',
51060
+ });
50595
51061
  }
50596
- resourceEntries.push({ entryName: resourceName, source });
51062
+ resourceEntries.push({ entryName: entry.entryName, source });
50597
51063
  }
50598
51064
  return {
50599
51065
  resourceBinary: null,
@@ -51090,6 +51556,86 @@ const INSTALLABLE_FIRMWARE_TARGET_IDS = new Set([
51090
51556
  ]);
51091
51557
  new Map(Object.entries(ProtocolV2FirmwareTargetType).flatMap(([key, value]) => INSTALLABLE_FIRMWARE_TARGET_IDS.has(value) ? [[key, value]] : []));
51092
51558
 
51559
+ let memoryHostSequence = 0;
51560
+ const createReference = (binary, prefix) => {
51561
+ const digest = utils.bytesToHex(sha256.sha256(new Uint8Array(binary)));
51562
+ return {
51563
+ artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
51564
+ size: binary.byteLength,
51565
+ sha256: digest,
51566
+ };
51567
+ };
51568
+ function prepareFirmwareUpdateV4MemoryHost({ sdk, plan, artifacts, }) {
51569
+ if (plan.executor !== 'v4') {
51570
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory host only supports V4 plans');
51571
+ }
51572
+ memoryHostSequence += 1;
51573
+ const hostId = `${Date.now()}:${memoryHostSequence}`;
51574
+ const binaries = new Map();
51575
+ const inputs = artifacts.map((input, artifactIndex) => {
51576
+ var _a;
51577
+ const artifactBinary = new Uint8Array(input.binary).slice();
51578
+ const artifact = createReference(artifactBinary.buffer, `${hostId}:artifact:${artifactIndex}`);
51579
+ binaries.set(artifact.artifactRef, artifactBinary);
51580
+ const materializedEntries = (_a = input.materializedEntries) === null || _a === void 0 ? void 0 : _a.map((entry, entryIndex) => {
51581
+ const entryArtifact = createReference(entry.binary, `${hostId}:entry:${artifactIndex}:${entryIndex}`);
51582
+ return {
51583
+ entryName: entry.entryName,
51584
+ artifact: entryArtifact,
51585
+ };
51586
+ });
51587
+ return Object.assign({ artifactId: input.artifactId, artifact }, ((materializedEntries === null || materializedEntries === void 0 ? void 0 : materializedEntries.length) ? { materializedEntries } : {}));
51588
+ });
51589
+ const preparedPlan = sdk.prepareFirmwareUpdatePlan({
51590
+ plan,
51591
+ leaseRef: `fwmemlease:${hostId}`,
51592
+ artifacts: inputs,
51593
+ });
51594
+ const readers = new Map();
51595
+ let readerSequence = 0;
51596
+ const artifactReader = {
51597
+ open({ artifactRef }) {
51598
+ const binary = binaries.get(artifactRef);
51599
+ if (!binary) {
51600
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory artifact is unavailable');
51601
+ }
51602
+ readerSequence += 1;
51603
+ const readerId = `fwmemreader:${hostId}:${readerSequence}`;
51604
+ readers.set(readerId, binary);
51605
+ return Promise.resolve({ readerId, size: binary.byteLength });
51606
+ },
51607
+ read({ readerId, offset, length }) {
51608
+ const binary = readers.get(readerId);
51609
+ if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
51610
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory artifact read is invalid');
51611
+ }
51612
+ const data = binary.slice(offset, offset + length).buffer;
51613
+ return Promise.resolve({
51614
+ data,
51615
+ bytesRead: data.byteLength,
51616
+ eof: offset + length === binary.byteLength,
51617
+ });
51618
+ },
51619
+ close({ readerId }) {
51620
+ readers.delete(readerId);
51621
+ return Promise.resolve();
51622
+ },
51623
+ };
51624
+ const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({
51625
+ artifactReader,
51626
+ preparedPlanDigest: preparedPlan.preparedPlanDigest,
51627
+ });
51628
+ return {
51629
+ preparedPlan,
51630
+ hostBindingGeneration,
51631
+ release: () => {
51632
+ sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
51633
+ readers.clear();
51634
+ binaries.clear();
51635
+ },
51636
+ };
51637
+ }
51638
+
51093
51639
  const Log$6 = getLogger(exports.LoggerNames.Method);
51094
51640
  const SESSION_ERROR$1 = 'session not found';
51095
51641
  const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
@@ -51116,14 +51662,34 @@ const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
51116
51662
  const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
51117
51663
  const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
51118
51664
  const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
51665
+ const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
51666
+ const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
51667
+ const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
51119
51668
  const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set(['se03', 'se04']);
51120
- function assertProtocolV2FirmwareTargetsSupported(deviceType, params) {
51669
+ const getProtocolV2ZipEntrySizes = (entry) => {
51670
+ var _a;
51671
+ const { compressedSize, uncompressedSize } = (_a = entry._data) !== null && _a !== void 0 ? _a : {};
51672
+ if (!Number.isSafeInteger(compressedSize) ||
51673
+ Number(compressedSize) < 0 ||
51674
+ !Number.isSafeInteger(uncompressedSize) ||
51675
+ Number(uncompressedSize) <= 0) {
51676
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource ZIP entry size is invalid: ${entry.name}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
51677
+ }
51678
+ return {
51679
+ compressedSize: Number(compressedSize),
51680
+ uncompressedSize: Number(uncompressedSize),
51681
+ };
51682
+ };
51683
+ function assertProtocolV2FirmwareTargetsSupported(deviceType, params, hasExplicitTargetSelection = false) {
51121
51684
  var _a;
51122
- const unsupportedTargets = new Set(((_a = params.targetsToUpdate) !== null && _a !== void 0 ? _a : []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target)));
51123
- if (params.se03Binary)
51685
+ const requestedTargets = new Set((_a = params.targetsToUpdate) !== null && _a !== void 0 ? _a : []);
51686
+ const unsupportedTargets = new Set(Array.from(requestedTargets).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target)));
51687
+ if (params.se03Binary && (!hasExplicitTargetSelection || requestedTargets.has('se03'))) {
51124
51688
  unsupportedTargets.add('se03');
51125
- if (params.se04Binary)
51689
+ }
51690
+ if (params.se04Binary && (!hasExplicitTargetSelection || requestedTargets.has('se04'))) {
51126
51691
  unsupportedTargets.add('se04');
51692
+ }
51127
51693
  if (!unsupportedTargets.size || deviceType === hdShared.EDeviceType.Pro2)
51128
51694
  return;
51129
51695
  const targetList = Array.from(unsupportedTargets).join(', ');
@@ -51187,6 +51753,14 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
51187
51753
  },
51188
51754
  };
51189
51755
  const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`));
51756
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
51757
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
51758
+ const isProtocolV2BootResourcePackagePath = (devicePath) => typeof devicePath === 'string' &&
51759
+ devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase() ===
51760
+ PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH;
51761
+ const resolveProtocolV2ResourceWritePath = (devicePath) => isProtocolV2BootResourcePackagePath(devicePath)
51762
+ ? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
51763
+ : devicePath;
51190
51764
  const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51191
51765
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
51192
51766
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
@@ -51197,6 +51771,10 @@ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51197
51771
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
51198
51772
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
51199
51773
  ]);
51774
+ const PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET = new Map(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => [
51775
+ PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId),
51776
+ target,
51777
+ ]));
51200
51778
  const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE = 'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
51201
51779
  const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map(Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value]));
51202
51780
  const PROTOCOL_V2_TARGET_STATUS_BY_DECODED_NAME = new Map([
@@ -51352,8 +51930,10 @@ const assertProtocolV2ReconnectIdentity = (expectedSerialNumber, actualSerialNum
51352
51930
  class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51353
51931
  constructor() {
51354
51932
  super(...arguments);
51933
+ this.protocolV2HasExplicitTargetSelection = false;
51355
51934
  this.protocolV2PreparedSources = [];
51356
51935
  this.protocolV2ExecutionInLoader = false;
51936
+ this.protocolV2BootResourceStagingSafe = false;
51357
51937
  this.protocolV2CompletedTargetVersions = new Map();
51358
51938
  this.protocolV2FinalStatusVerified = false;
51359
51939
  }
@@ -51361,13 +51941,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51361
51941
  return ['V2'];
51362
51942
  }
51363
51943
  init() {
51364
- var _a, _b, _c;
51944
+ var _a, _b;
51365
51945
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
51366
51946
  this.requireDeviceMode = [];
51367
51947
  this.unlockPolicy = 'unlock-before-run';
51368
51948
  this.useDevicePassphraseState = false;
51369
51949
  this.skipForceUpdateCheck = true;
51370
51950
  const { payload } = this;
51951
+ this.protocolV2HasExplicitTargetSelection = payload.targetsToUpdate !== undefined;
51371
51952
  if (typeof payload.retryCount !== 'number') {
51372
51953
  payload.retryCount = PROTOCOL_V2_CONNECT_RETRY_COUNT;
51373
51954
  }
@@ -51392,54 +51973,85 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51392
51973
  { name: 'se02Binary', type: 'buffer' },
51393
51974
  { name: 'se03Binary', type: 'buffer' },
51394
51975
  { name: 'se04Binary', type: 'buffer' },
51976
+ { name: 'resourceArchiveBinary', type: 'buffer' },
51395
51977
  { name: 'firmwareType', type: 'string' },
51396
51978
  { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
51397
51979
  { name: 'platform', type: 'string' },
51398
51980
  { name: 'expectedDeviceId', type: 'string' },
51399
- { name: 'resourceFiles', type: 'array', allowEmpty: true },
51400
51981
  ]);
51401
51982
  if (payload.expectedDeviceId !== undefined &&
51402
51983
  (payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)) {
51403
51984
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Protocol V2 expected device identity is invalid');
51404
51985
  }
51405
- const hostBinding = payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
51406
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
51986
+ const preparedPlan = payload.preparedPlan
51987
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
51407
51988
  : undefined;
51408
- if (hostBinding) {
51989
+ const hasLocalArtifacts = [
51990
+ payload.bootloaderBinary,
51991
+ payload.romloaderBinary,
51992
+ payload.applicationP1Binary,
51993
+ payload.applicationP2Binary,
51994
+ payload.coprocessorBinary,
51995
+ payload.se01Binary,
51996
+ payload.se02Binary,
51997
+ payload.se03Binary,
51998
+ payload.se04Binary,
51999
+ payload.resourceArchiveBinary,
52000
+ ].some(Boolean);
52001
+ if (preparedPlan && hasLocalArtifacts) {
52002
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy or local firmware inputs');
52003
+ }
52004
+ let { artifactReader } = payload;
52005
+ if (preparedPlan) {
52006
+ artifactReader = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader;
52007
+ }
52008
+ else if (payload.hostBindingGeneration !== undefined) {
52009
+ artifactReader = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration).artifactReader;
52010
+ }
52011
+ if (preparedPlan) {
51409
52012
  assertFirmwareUpdatePreparedPlanBinding({
51410
- preparedPlan: payload.preparedPlan,
52013
+ preparedPlan,
51411
52014
  executor: 'v4',
51412
52015
  platform: payload.platform,
51413
- scopeTargets: [
51414
- 'boot',
51415
- 'app_v1',
51416
- 'app_v2',
51417
- 'coprocessor',
51418
- 'resource',
51419
- 'se01',
51420
- 'se02',
51421
- 'se03',
51422
- 'se04',
51423
- ],
51424
- bindings: [
51425
- ...Object.entries((_a = payload.componentArtifacts) !== null && _a !== void 0 ? _a : {}).flatMap(([target, artifact]) => artifact
51426
- ? [
51427
- {
51428
- target: target,
51429
- artifact,
51430
- },
51431
- ]
51432
- : []),
51433
- ...((_b = payload.resourceBundleArtifacts) !== null && _b !== void 0 ? _b : []).map((entry) => ({
51434
- target: 'resource',
51435
- logicalName: entry.name,
51436
- artifact: entry.artifact,
51437
- })),
51438
- ],
52016
+ scopeTargets: [],
52017
+ bindings: [],
51439
52018
  });
52019
+ if (payload.componentArtifacts) {
52020
+ const componentBindings = Object.entries(payload.componentArtifacts).flatMap(([target, artifact]) => artifact
52021
+ ? [
52022
+ {
52023
+ target: target,
52024
+ artifact: artifact,
52025
+ },
52026
+ ]
52027
+ : []);
52028
+ assertFirmwareUpdatePreparedPlanBinding({
52029
+ preparedPlan,
52030
+ executor: 'v4',
52031
+ platform: payload.platform,
52032
+ scopeTargets: componentBindings.map(binding => binding.target),
52033
+ bindings: componentBindings,
52034
+ });
52035
+ }
52036
+ }
52037
+ const preparedExpectedTargetVersions = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.artifacts.reduce((result, artifact) => {
52038
+ if (artifact.role === 'component' &&
52039
+ artifact.target !== 'resource' &&
52040
+ artifact.targetVersion &&
52041
+ PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.has(artifact.target)) {
52042
+ result[artifact.target] = artifact.targetVersion;
52043
+ }
52044
+ return result;
52045
+ }, {});
52046
+ const localTargetsToUpdate = (_a = payload.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.map((target) => target === 'boot_resources' ? 'resource' : target);
52047
+ if (payload.resourceArchiveBinary &&
52048
+ localTargetsToUpdate &&
52049
+ !localTargetsToUpdate.includes('resource')) {
52050
+ localTargetsToUpdate.push('resource');
51440
52051
  }
52052
+ const resolvedLocalTargetsToUpdate = localTargetsToUpdate !== null && localTargetsToUpdate !== void 0 ? localTargetsToUpdate : (payload.resourceArchiveBinary ? ['resource'] : undefined);
51441
52053
  this.params = {
51442
- preparedPlan: payload.preparedPlan,
52054
+ preparedPlan,
51443
52055
  chunkSize: payload.chunkSize,
51444
52056
  forcedUpdateRes: payload.forcedUpdateRes,
51445
52057
  bootloaderBinary: payload.bootloaderBinary,
@@ -51451,15 +52063,18 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51451
52063
  se02Binary: payload.se02Binary,
51452
52064
  se03Binary: payload.se03Binary,
51453
52065
  se04Binary: payload.se04Binary,
51454
- resourceFiles: payload.resourceFiles,
51455
- firmwareType: payload.firmwareType,
51456
- targetsToUpdate: payload.targetsToUpdate,
51457
- expectedTargetVersions: payload.expectedTargetVersions,
52066
+ resourceArchiveBinary: payload.resourceArchiveBinary,
52067
+ firmwareType: (_b = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _b !== void 0 ? _b : payload.firmwareType,
52068
+ targetsToUpdate: preparedPlan
52069
+ ? [...preparedPlan.targetsToUpdate]
52070
+ : resolvedLocalTargetsToUpdate,
52071
+ expectedTargetVersions: preparedPlan
52072
+ ? preparedExpectedTargetVersions
52073
+ : payload.expectedTargetVersions,
51458
52074
  platform: payload.platform,
51459
52075
  expectedDeviceId: payload.expectedDeviceId,
51460
- artifactReader: (_c = hostBinding === null || hostBinding === void 0 ? void 0 : hostBinding.artifactReader) !== null && _c !== void 0 ? _c : payload.artifactReader,
51461
- componentArtifacts: payload.componentArtifacts,
51462
- resourceBundleArtifacts: payload.resourceBundleArtifacts,
52076
+ artifactReader,
52077
+ componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
51463
52078
  };
51464
52079
  }
51465
52080
  getProtocolV2FirmwareChunkSize(direction, filePath) {
@@ -51490,7 +52105,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51490
52105
  });
51491
52106
  }
51492
52107
  runProtocolV2() {
51493
- var _a, _b, _c, _d, _e;
52108
+ var _a, _b, _c, _d, _e, _f, _g, _h;
51494
52109
  return __awaiter(this, void 0, void 0, function* () {
51495
52110
  yield this.captureProtocolV2PhysicalIdentity();
51496
52111
  const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
@@ -51498,89 +52113,96 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51498
52113
  const capabilityDeviceType = currentDeviceType === hdShared.EDeviceType.Pro2 || currentDeviceType === hdShared.EDeviceType.Neo
51499
52114
  ? currentDeviceType
51500
52115
  : getDeviceType(deviceFeatures);
51501
- assertProtocolV2FirmwareTargetsSupported(capabilityDeviceType, this.params);
52116
+ assertProtocolV2FirmwareTargetsSupported(capabilityDeviceType, this.params, this.protocolV2HasExplicitTargetSelection);
51502
52117
  const deviceFirmwareType = getFirmwareType(deviceFeatures);
51503
52118
  const firmwareType = (_a = this.params.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
51504
52119
  this.validateExpectedTargetVersions();
51505
- if ((this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
51506
- ((_b = this.params.resourceBundleArtifacts) === null || _b === void 0 ? void 0 : _b.length)) {
52120
+ if (!this.params.preparedPlan &&
52121
+ this.params.resourceArchiveBinary &&
52122
+ ((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('resource'))) {
52123
+ const localMemoryHost = yield this.prepareProtocolV2LocalMemoryHost({
52124
+ features: deviceFeatures,
52125
+ firmwareType,
52126
+ });
52127
+ try {
52128
+ return yield this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
52129
+ }
52130
+ finally {
52131
+ localMemoryHost.release();
52132
+ }
52133
+ }
52134
+ const hasPreparedComponentArtifacts = Object.values((_c = this.params.componentArtifacts) !== null && _c !== void 0 ? _c : {}).some(Boolean);
52135
+ if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
51507
52136
  return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
51508
52137
  }
51509
- const hasExplicitResourceFiles = !!((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length);
51510
- const wantsStableResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
51511
- const wantsBootResources = !!((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.includes('boot_resources'));
51512
- const needsRemoteResources = !hasExplicitResourceFiles && wantsStableResources;
51513
- const needsRemoteBootResources = !hasExplicitResourceFiles && (wantsBootResources || wantsStableResources);
52138
+ const wantsResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
51514
52139
  let fwBinaryMap = [];
51515
52140
  let bootloaderBinary = null;
51516
52141
  let installItems;
51517
- let resourceBundles;
51518
52142
  try {
51519
52143
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartDownloadFirmware);
51520
- resourceBundles = this.prepareExplicitProtocolV2ResourceFiles();
51521
52144
  fwBinaryMap = this.collectExplicitTargetBinaries();
51522
52145
  bootloaderBinary = this.prepareBootloaderBinary();
51523
- const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
51524
- if ((needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) &&
52146
+ const explicitInstallItems = this.buildProtocolV2InstallItems({
52147
+ bootloaderBinary,
52148
+ fwBinaryMap,
52149
+ });
52150
+ const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
52151
+ const needsRemoteFirmware = ((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.length)
52152
+ ? missingFirmwareTargets.length > 0
52153
+ : explicitInstallItems.length === 0;
52154
+ if (wantsResources) {
52155
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource archive must be provided through a local or external PreparedPlan', {
52156
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52157
+ });
52158
+ }
52159
+ if (needsRemoteFirmware &&
51525
52160
  (this.params.artifactReader ||
51526
52161
  DataManager.getSettings('firmwareManifestMode') === 'external-only')) {
51527
52162
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware artifacts must be prepared by the external firmware host', {
51528
52163
  firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
51529
52164
  });
51530
52165
  }
51531
- if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
51532
- yield DataManager.forceReloadData({
51533
- requireResources: needsRemoteResources || needsRemoteBootResources,
51534
- });
52166
+ if (needsRemoteFirmware) {
52167
+ yield DataManager.forceReloadData();
51535
52168
  }
51536
52169
  if (needsRemoteFirmware) {
51537
- const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures);
52170
+ const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures, explicitInstallItems);
51538
52171
  bootloaderBinary = remoteBinaries.bootloaderBinary;
51539
52172
  fwBinaryMap = remoteBinaries.fwBinaryMap;
51540
52173
  installItems = remoteBinaries.installItems;
51541
52174
  }
51542
- const bootResourceFiles = yield this.prepareProtocolV2BootResources();
51543
- if (bootResourceFiles === null || bootResourceFiles === void 0 ? void 0 : bootResourceFiles.length) {
51544
- resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, bootResourceFiles);
51545
- }
51546
- if (!needsRemoteResources) {
51547
- this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
52175
+ else {
52176
+ const selectedInstallItems = this.filterProtocolV2LocalInstallItems(explicitInstallItems);
52177
+ bootloaderBinary =
52178
+ (_g = (_f = selectedInstallItems.find(item => item.kind === 'bootloader')) === null || _f === void 0 ? void 0 : _f.binary) !== null && _g !== void 0 ? _g : null;
52179
+ fwBinaryMap = selectedInstallItems
52180
+ .filter(item => item.kind === 'firmware')
52181
+ .map(item => ({
52182
+ fileName: item.fileName,
52183
+ binary: item.binary,
52184
+ targetId: item.targetId,
52185
+ }));
51548
52186
  }
52187
+ this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51549
52188
  }
51550
52189
  catch (err) {
52190
+ if (typeof err === 'object' &&
52191
+ err !== null &&
52192
+ 'params' in err &&
52193
+ ((_h = err.params) === null || _h === void 0 ? void 0 : _h.firmwareUpdateCode) === 'FirmwareArtifactsNotPrepared') {
52194
+ throw err;
52195
+ }
51551
52196
  if (err instanceof hdShared.HardwareError && err.errorCode === hdShared.HardwareErrorCode.NetworkError) {
51552
52197
  throw err;
51553
52198
  }
51554
52199
  throw normalizeFirmwarePreparationError(err);
51555
52200
  }
51556
- if (!bootloaderBinary &&
51557
- fwBinaryMap.length === 0 &&
51558
- !(installItems === null || installItems === void 0 ? void 0 : installItems.length) &&
51559
- !(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) &&
51560
- !needsRemoteResources) {
52201
+ if (!bootloaderBinary && fwBinaryMap.length === 0 && !(installItems === null || installItems === void 0 ? void 0 : installItems.length)) {
51561
52202
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
51562
52203
  }
51563
- if (needsRemoteResources) {
51564
- const enteredBootloader = yield this.enterProtocolV2BootloaderMode();
51565
- try {
51566
- const stableResources = yield this.prepareProtocolV2ResourceBundles();
51567
- resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, stableResources);
51568
- this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51569
- }
51570
- catch (err) {
51571
- if (enteredBootloader) {
51572
- try {
51573
- yield this.exitProtocolV2BootloaderToNormal();
51574
- }
51575
- catch (restoreError) {
51576
- Log$6.warn('[FirmwareUpdateV4] failed to restore App mode after resource preparation error:', restoreError);
51577
- }
51578
- }
51579
- throw normalizeFirmwarePreparationError(err);
51580
- }
51581
- }
51582
- return this.executeProtocolV2Update(Object.assign(Object.assign({ fwBinaryMap,
51583
- bootloaderBinary }, (installItems ? { installItems } : undefined)), ((resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) ? { resourceBundles } : undefined)));
52204
+ return this.executeProtocolV2Update(Object.assign({ fwBinaryMap,
52205
+ bootloaderBinary }, (installItems ? { installItems } : undefined)));
51584
52206
  });
51585
52207
  }
51586
52208
  openProtocolV2PreparedSource(artifact) {
@@ -51617,6 +52239,34 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51617
52239
  prepareProtocolV2InstallSources(firmwareType, features) {
51618
52240
  var _a, _b;
51619
52241
  return __awaiter(this, void 0, void 0, function* () {
52242
+ if (this.params.preparedPlan) {
52243
+ const requestedTargets = new Set(this.params.preparedPlan.targetsToUpdate.filter((target) => target !== 'resource'));
52244
+ const installSources = [];
52245
+ const preparedTargets = new Set();
52246
+ for (const artifact of this.params.preparedPlan.artifacts) {
52247
+ if (artifact.target !== 'resource') {
52248
+ const target = artifact.target;
52249
+ const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
52250
+ if (!requestedTargets.has(target) ||
52251
+ artifact.role !== 'component' ||
52252
+ artifact.container !== 'raw' ||
52253
+ !installTarget ||
52254
+ preparedTargets.has(target)) {
52255
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52256
+ }
52257
+ installSources.push(Object.assign(Object.assign({}, installTarget), { source: yield this.openProtocolV2PreparedSource(artifact.artifact) }));
52258
+ preparedTargets.add(target);
52259
+ }
52260
+ }
52261
+ const missingTarget = Array.from(requestedTargets).find(target => !preparedTargets.has(target));
52262
+ if (missingTarget) {
52263
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 ${missingTarget} artifact is not prepared`, {
52264
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52265
+ artifactName: missingTarget,
52266
+ });
52267
+ }
52268
+ return installSources;
52269
+ }
51620
52270
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
51621
52271
  if (!release) {
51622
52272
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware release is unavailable');
@@ -51647,39 +52297,285 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51647
52297
  return installSources;
51648
52298
  });
51649
52299
  }
51650
- prepareProtocolV2ResourceSources(firmwareType, features) {
51651
- var _a, _b, _c;
52300
+ prepareProtocolV2LocalMemoryHost({ features, firmwareType, }) {
52301
+ var _a, _b;
51652
52302
  return __awaiter(this, void 0, void 0, function* () {
51653
- const preparedArtifacts = (_a = this.params.resourceBundleArtifacts) !== null && _a !== void 0 ? _a : [];
51654
- const resourceRequested = ((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('resource')) || preparedArtifacts.length > 0;
52303
+ const availableInstallItems = this.buildProtocolV2InstallItems({
52304
+ bootloaderBinary: this.prepareBootloaderBinary(),
52305
+ fwBinaryMap: this.collectExplicitTargetBinaries(),
52306
+ });
52307
+ const requestedComponentTargets = new Set(((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : []).filter((target) => target !== 'resource' && target !== 'boot_resources'));
52308
+ const localComponentTargets = new Set(availableInstallItems.flatMap(item => {
52309
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52310
+ return target ? [target] : [];
52311
+ }));
52312
+ const missingTarget = Array.from(requestedComponentTargets).find(target => !localComponentTargets.has(target));
52313
+ if (missingTarget) {
52314
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local update has no binary for requested target ${missingTarget}`, {
52315
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52316
+ artifactName: missingTarget,
52317
+ });
52318
+ }
52319
+ const installItems = this.filterProtocolV2LocalInstallItems(availableInstallItems);
52320
+ const planArtifacts = [];
52321
+ const memoryArtifacts = [];
52322
+ for (const item of installItems) {
52323
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52324
+ if (!target || item.binary.byteLength <= 0) {
52325
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local firmware artifact is invalid: ${item.fileName}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52326
+ }
52327
+ const artifactId = `component:${target}`;
52328
+ planArtifacts.push(Object.assign({ artifactId,
52329
+ target, container: 'raw', logicalName: item.fileName, expectedSize: item.binary.byteLength, expectedSha256: bytesToHex(sha256.sha256(new Uint8Array(item.binary))) }, (((_b = this.params.expectedTargetVersions) === null || _b === void 0 ? void 0 : _b[target])
52330
+ ? { targetVersion: this.params.expectedTargetVersions[target] }
52331
+ : {})));
52332
+ memoryArtifacts.push({ artifactId, binary: item.binary });
52333
+ }
52334
+ const resourceArchive = yield this.prepareProtocolV2LocalResourceArchive(this.params.resourceArchiveBinary);
52335
+ const resourceArtifactId = 'resource:archive';
52336
+ planArtifacts.push({
52337
+ artifactId: resourceArtifactId,
52338
+ target: 'resource',
52339
+ container: 'zip',
52340
+ logicalName: 'protocol-v2-local-resource-archive',
52341
+ expectedSize: resourceArchive.binary.byteLength,
52342
+ expectedSha256: bytesToHex(sha256.sha256(new Uint8Array(resourceArchive.binary))),
52343
+ });
52344
+ memoryArtifacts.push({
52345
+ artifactId: resourceArtifactId,
52346
+ binary: resourceArchive.binary,
52347
+ materializedEntries: resourceArchive.materializedEntries,
52348
+ });
52349
+ const plan = buildProtocolV2LocalFirmwareUpdatePlan({
52350
+ features,
52351
+ firmwareType,
52352
+ platform: this.params.platform,
52353
+ artifacts: planArtifacts,
52354
+ });
52355
+ let memoryHost;
52356
+ try {
52357
+ memoryHost = prepareFirmwareUpdateV4MemoryHost({
52358
+ sdk: {
52359
+ prepareFirmwareUpdatePlan,
52360
+ registerFirmwareUpdateHostBinding,
52361
+ unregisterFirmwareUpdateHostBinding,
52362
+ },
52363
+ plan,
52364
+ artifacts: memoryArtifacts,
52365
+ });
52366
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
52367
+ assertFirmwareUpdatePreparedPlanBinding({
52368
+ preparedPlan,
52369
+ executor: 'v4',
52370
+ platform: this.params.platform,
52371
+ scopeTargets: [],
52372
+ bindings: [],
52373
+ });
52374
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
52375
+ preparedPlan,
52376
+ deviceIdentity: this.protocolV2ExpectedSerialNumber,
52377
+ deviceModel: this.getProtocolV2PreparedPlanDeviceModel(features),
52378
+ });
52379
+ const hostBinding = resolveFirmwareUpdateHostBinding(memoryHost.hostBindingGeneration, preparedPlan.preparedPlanDigest);
52380
+ this.params.preparedPlan = preparedPlan;
52381
+ this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate];
52382
+ this.params.artifactReader = hostBinding.artifactReader;
52383
+ this.params.componentArtifacts = undefined;
52384
+ return memoryHost;
52385
+ }
52386
+ catch (error) {
52387
+ memoryHost === null || memoryHost === void 0 ? void 0 : memoryHost.release();
52388
+ throw error;
52389
+ }
52390
+ });
52391
+ }
52392
+ prepareProtocolV2LocalResourceArchive(binary) {
52393
+ var _a;
52394
+ return __awaiter(this, void 0, void 0, function* () {
52395
+ if (binary.byteLength <= 0 || binary.byteLength > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52396
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP archive size is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52397
+ }
52398
+ let zip;
52399
+ try {
52400
+ zip = yield JSZip__default["default"].loadAsync(binary);
52401
+ }
52402
+ catch (_b) {
52403
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource ZIP cannot be parsed', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52404
+ }
52405
+ const zipEntries = Object.values(zip.files);
52406
+ if (zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)) {
52407
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP contains an unsafe entry path', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52408
+ }
52409
+ const entries = zipEntries.filter(entry => !entry.dir);
52410
+ if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
52411
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP entry set is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52412
+ }
52413
+ let declaredUncompressedSize = 0;
52414
+ let declaredCompressedSize = 0;
52415
+ for (const entry of entries) {
52416
+ const sizes = getProtocolV2ZipEntrySizes(entry);
52417
+ declaredCompressedSize += sizes.compressedSize;
52418
+ declaredUncompressedSize += sizes.uncompressedSize;
52419
+ const entryLimit = entry.name === 'manifest.json'
52420
+ ? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
52421
+ : PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
52422
+ if (sizes.uncompressedSize > entryLimit ||
52423
+ declaredCompressedSize > binary.byteLength ||
52424
+ declaredUncompressedSize >
52425
+ PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES) {
52426
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP declared size exceeds the allowed limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52427
+ }
52428
+ }
52429
+ const manifestEntry = zip.file('manifest.json');
52430
+ if (!manifestEntry) {
52431
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP has no manifest.json', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52432
+ }
52433
+ const manifestBinary = yield manifestEntry.async('arraybuffer');
52434
+ if (manifestBinary.byteLength <= 0 ||
52435
+ manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES) {
52436
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource manifest size is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52437
+ }
52438
+ let manifestValue;
52439
+ try {
52440
+ manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
52441
+ }
52442
+ catch (error) {
52443
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource manifest is invalid: ${String(error)}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52444
+ }
52445
+ const manifest = parseProtocolV2ResourceManifest(manifestValue);
52446
+ const selectedFiles = selectProtocolV2ResourceManifestFiles({
52447
+ manifest,
52448
+ targetsToUpdate: (_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : [],
52449
+ });
52450
+ const expectedEntryNames = new Set([
52451
+ 'manifest.json',
52452
+ ...selectedFiles.map(file => file.archive_path),
52453
+ ]);
52454
+ if (entries.length !== expectedEntryNames.size ||
52455
+ entries.some(entry => !expectedEntryNames.has(entry.name))) {
52456
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP contains an unexpected entry', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52457
+ }
52458
+ let totalSize = 0;
52459
+ const materializedEntries = [
52460
+ { entryName: 'manifest.json', binary: manifestBinary },
52461
+ ];
52462
+ for (const file of selectedFiles) {
52463
+ const entry = zip.file(file.archive_path);
52464
+ if (!entry) {
52465
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource ZIP is missing ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52466
+ }
52467
+ const { uncompressedSize } = getProtocolV2ZipEntrySizes(entry);
52468
+ totalSize += uncompressedSize;
52469
+ if (uncompressedSize !== file.size || totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52470
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource file declared size is invalid: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52471
+ }
52472
+ const fileBinary = yield entry.async('arraybuffer');
52473
+ const digest = bytesToHex(sha256.sha256(new Uint8Array(fileBinary)));
52474
+ if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
52475
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource file does not match manifest: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52476
+ }
52477
+ materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
52478
+ }
52479
+ return { binary, materializedEntries };
52480
+ });
52481
+ }
52482
+ prepareProtocolV2ResourceSources() {
52483
+ var _a, _b;
52484
+ return __awaiter(this, void 0, void 0, function* () {
52485
+ const resourceRequested = (_b = (_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource')) !== null && _b !== void 0 ? _b : false;
51655
52486
  if (!resourceRequested) {
51656
52487
  return [];
51657
52488
  }
51658
- const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
51659
- const descriptors = (_c = release === null || release === void 0 ? void 0 : release.resourceBundles) !== null && _c !== void 0 ? _c : [];
51660
- const artifactByName = new Map(preparedArtifacts.map(item => [item.name, item.artifact]));
52489
+ const archiveSources = yield this.prepareProtocolV2ResourceArchiveSources();
52490
+ if (archiveSources) {
52491
+ return archiveSources;
52492
+ }
52493
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource archive is not prepared', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52494
+ });
52495
+ }
52496
+ prepareProtocolV2ResourceArchiveSources() {
52497
+ var _a, _b, _c, _d;
52498
+ return __awaiter(this, void 0, void 0, function* () {
52499
+ const archiveArtifacts = (_b = (_a = this.params.preparedPlan) === null || _a === void 0 ? void 0 : _a.artifacts.filter(artifact => artifact.role === 'resourceBundle' &&
52500
+ artifact.target === 'resource' &&
52501
+ artifact.container === 'zip')) !== null && _b !== void 0 ? _b : [];
52502
+ if (archiveArtifacts.length === 0) {
52503
+ return undefined;
52504
+ }
52505
+ if (archiveArtifacts.length !== 1) {
52506
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared plan must contain exactly one resource archive', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52507
+ }
52508
+ const archiveArtifact = archiveArtifacts[0];
52509
+ const archiveSource = yield this.openProtocolV2PreparedSource(archiveArtifact.artifact);
52510
+ if (archiveSource.size > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52511
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive exceeds the total size limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52512
+ }
52513
+ let archiveBinary;
52514
+ try {
52515
+ archiveBinary = yield readFirmwareByteSourceFully(archiveSource);
52516
+ }
52517
+ finally {
52518
+ yield archiveSource.close();
52519
+ }
52520
+ const archiveDigest = bytesToHex(sha256.sha256(new Uint8Array(archiveBinary)));
52521
+ if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
52522
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive does not match its approved receipt', { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52523
+ }
52524
+ const verifiedArchive = yield this.prepareProtocolV2LocalResourceArchive(archiveBinary);
52525
+ const entries = (_c = archiveArtifact.materializedEntries) !== null && _c !== void 0 ? _c : [];
52526
+ const entriesByName = new Map(entries.map(entry => [entry.entryName, entry]));
52527
+ if (entries.length !== verifiedArchive.materializedEntries.length ||
52528
+ verifiedArchive.materializedEntries.some(entry => {
52529
+ const preparedEntry = entriesByName.get(entry.entryName);
52530
+ const digest = bytesToHex(sha256.sha256(new Uint8Array(entry.binary)));
52531
+ return (!preparedEntry ||
52532
+ preparedEntry.artifact.size !== entry.binary.byteLength ||
52533
+ preparedEntry.artifact.sha256.toLowerCase() !== digest);
52534
+ })) {
52535
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource entries do not match the approved archive', { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52536
+ }
52537
+ const verifiedEntriesByName = new Map(verifiedArchive.materializedEntries.map(entry => [entry.entryName, entry.binary]));
52538
+ const manifestBinary = verifiedEntriesByName.get('manifest.json');
52539
+ if (!manifestBinary) {
52540
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive has no valid manifest.json', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52541
+ }
52542
+ let manifestValue;
52543
+ try {
52544
+ manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
52545
+ }
52546
+ catch (error) {
52547
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared resource manifest is invalid: ${String(error)}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52548
+ }
52549
+ const manifest = parseProtocolV2ResourceManifest(manifestValue);
52550
+ const selectedFiles = selectProtocolV2ResourceManifestFiles({
52551
+ manifest,
52552
+ targetsToUpdate: (_d = this.params.targetsToUpdate) !== null && _d !== void 0 ? _d : [],
52553
+ });
52554
+ if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
52555
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive contains too many files', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52556
+ }
52557
+ let totalSize = 0;
51661
52558
  const sources = [];
51662
- for (const descriptor of descriptors) {
51663
- const artifact = artifactByName.get(descriptor.name);
51664
- if (!artifact) {
51665
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 resource bundle ${descriptor.name} is not prepared`, {
51666
- firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
51667
- artifactName: descriptor.name,
51668
- });
52559
+ for (const [index, file] of selectedFiles.entries()) {
52560
+ const binary = verifiedEntriesByName.get(file.archive_path);
52561
+ if (!binary || binary.byteLength !== file.size) {
52562
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
51669
52563
  }
51670
- const devicePath = validateProtocolV2FilesystemPath(descriptor.devicePath, 'resourceBundles[].devicePath');
51671
- sources.push({
51672
- name: descriptor.name,
51673
- source: yield this.openProtocolV2PreparedSource(artifact),
51674
- devicePath,
51675
- version: descriptor.version,
51676
- payloadHash: descriptor.payloadHash,
51677
- headerHash: descriptor.headerHash,
51678
- });
51679
- artifactByName.delete(descriptor.name);
51680
- }
51681
- if (artifactByName.size > 0) {
51682
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`);
52564
+ totalSize += binary.byteLength;
52565
+ if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52566
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive exceeds the total size limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52567
+ }
52568
+ const source = yield this.openProtocolV2MemorySource(binary);
52569
+ const header = source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
52570
+ ? parseProtocolV2OkppHeader(new Uint8Array(yield source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE)))
52571
+ : null;
52572
+ sources.push(Object.assign({ name: file.original_name || `resource-${index}`, source, devicePath: file.device_path }, (header
52573
+ ? {
52574
+ version: header.version,
52575
+ payloadHash: header.payloadHash,
52576
+ headerHash: header.headerHash,
52577
+ }
52578
+ : {})));
51683
52579
  }
51684
52580
  return sources;
51685
52581
  });
@@ -51689,7 +52585,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51689
52585
  try {
51690
52586
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartDownloadFirmware);
51691
52587
  const installSources = yield this.prepareProtocolV2InstallSources(firmwareType, features);
51692
- const resourceSources = yield this.prepareProtocolV2ResourceSources(firmwareType, features);
52588
+ const resourceSources = yield this.prepareProtocolV2ResourceSources();
51693
52589
  if (installSources.length === 0 && resourceSources.length === 0) {
51694
52590
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
51695
52591
  }
@@ -51773,15 +52669,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51773
52669
  }
51774
52670
  }
51775
52671
  getProtocolV2RequestedTargets() {
51776
- var _a, _b, _c, _d;
52672
+ var _a, _b, _c;
51777
52673
  if ((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.length) {
51778
52674
  return [...new Set(this.params.targetsToUpdate)];
51779
52675
  }
51780
- const targets = new Set();
51781
- Object.keys((_b = this.params.componentArtifacts) !== null && _b !== void 0 ? _b : {}).forEach(target => targets.add(target));
51782
- if (((_c = this.params.resourceBundleArtifacts) === null || _c === void 0 ? void 0 : _c.length) || ((_d = this.params.resourceFiles) === null || _d === void 0 ? void 0 : _d.length)) {
51783
- targets.add('resource');
52676
+ if ((_b = this.params.preparedPlan) === null || _b === void 0 ? void 0 : _b.targetsToUpdate.length) {
52677
+ return [...new Set(this.params.preparedPlan.targetsToUpdate)];
51784
52678
  }
52679
+ const targets = new Set();
52680
+ Object.keys((_c = this.params.componentArtifacts) !== null && _c !== void 0 ? _c : {}).forEach(target => targets.add(target));
51785
52681
  if (this.params.bootloaderBinary)
51786
52682
  targets.add('boot');
51787
52683
  if (this.params.applicationP1Binary)
@@ -51818,8 +52714,26 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51818
52714
  const serialNumber = (_b = (_a = deviceInfo.hw) === null || _a === void 0 ? void 0 : _a.serial_no) === null || _b === void 0 ? void 0 : _b.trim();
51819
52715
  return serialNumber || undefined;
51820
52716
  }
52717
+ getProtocolV2PreparedPlanDeviceModel(features) {
52718
+ const currentDeviceType = this.device.getCurrentDeviceType();
52719
+ const featureDeviceType = features ? getDeviceType(features) : undefined;
52720
+ const deviceType = currentDeviceType === hdShared.EDeviceType.Pro2 || currentDeviceType === hdShared.EDeviceType.Neo
52721
+ ? currentDeviceType
52722
+ : featureDeviceType;
52723
+ return deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo
52724
+ ? String(deviceType)
52725
+ : undefined;
52726
+ }
52727
+ getProtocolV2ConnectionRoute() {
52728
+ var _a, _b, _c, _d, _e;
52729
+ const descriptor = this.device.originalDescriptor;
52730
+ return (((_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.path) === null || _a === void 0 ? void 0 : _a.trim()) ||
52731
+ ((_d = (_c = (_b = this.device).getConnectId) === null || _c === void 0 ? void 0 : _c.call(_b)) === null || _d === void 0 ? void 0 : _d.trim()) ||
52732
+ ((_e = descriptor === null || descriptor === void 0 ? void 0 : descriptor.id) === null || _e === void 0 ? void 0 : _e.trim()) ||
52733
+ undefined);
52734
+ }
51821
52735
  assertProtocolV2DeviceInfoIdentity(deviceInfo) {
51822
- assertProtocolV2ReconnectIdentity(this.protocolV2ExpectedSerialNumber, this.getProtocolV2SerialNumber(deviceInfo), this.protocolV2ExpectedPath, this.device.originalDescriptor.path);
52736
+ assertProtocolV2ReconnectIdentity(this.protocolV2ExpectedSerialNumber, this.getProtocolV2SerialNumber(deviceInfo), this.protocolV2ExpectedPath, this.getProtocolV2ConnectionRoute());
51823
52737
  }
51824
52738
  getProtocolV2DeviceInfoTimeout() {
51825
52739
  var _a;
@@ -51836,19 +52750,20 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51836
52750
  });
51837
52751
  }
51838
52752
  captureProtocolV2PhysicalIdentity() {
51839
- var _a, _b, _c, _d, _e;
52753
+ var _a, _b, _c;
51840
52754
  return __awaiter(this, void 0, void 0, function* () {
51841
52755
  const deviceInfo = yield this.requestProtocolV2PhysicalIdentity();
51842
52756
  const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
51843
- const path = ((_b = (_a = this.device.originalDescriptor) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.trim()) || undefined;
51844
- if ((_c = this.params) === null || _c === void 0 ? void 0 : _c.preparedPlan) {
52757
+ const path = this.getProtocolV2ConnectionRoute();
52758
+ if ((_a = this.params) === null || _a === void 0 ? void 0 : _a.preparedPlan) {
51845
52759
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
51846
52760
  preparedPlan: this.params.preparedPlan,
51847
52761
  deviceIdentity: serialNumber,
52762
+ deviceModel: this.getProtocolV2PreparedPlanDeviceModel(this.device.features),
51848
52763
  });
51849
52764
  }
51850
- else if ((_d = this.params) === null || _d === void 0 ? void 0 : _d.expectedDeviceId) {
51851
- assertProtocolV2ReconnectIdentity((_e = this.params) === null || _e === void 0 ? void 0 : _e.expectedDeviceId, serialNumber);
52765
+ else if ((_b = this.params) === null || _b === void 0 ? void 0 : _b.expectedDeviceId) {
52766
+ assertProtocolV2ReconnectIdentity((_c = this.params) === null || _c === void 0 ? void 0 : _c.expectedDeviceId, serialNumber);
51852
52767
  }
51853
52768
  else {
51854
52769
  assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
@@ -51868,11 +52783,26 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51868
52783
  var _a;
51869
52784
  return (_a = this.params.bootloaderBinary) !== null && _a !== void 0 ? _a : null;
51870
52785
  }
51871
- hasExplicitProtocolV2Payload(fwBinaryMap) {
52786
+ getMissingProtocolV2FirmwareTargets(installItems) {
51872
52787
  var _a;
51873
- return (!!((_a = this.params.resourceFiles) === null || _a === void 0 ? void 0 : _a.length) ||
51874
- !!this.params.bootloaderBinary ||
51875
- fwBinaryMap.length > 0);
52788
+ const preparedTargets = new Set(installItems.flatMap(item => {
52789
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52790
+ return target ? [target] : [];
52791
+ }));
52792
+ return ((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : [])
52793
+ .filter((target) => target !== 'resource' && target !== 'boot_resources')
52794
+ .filter(target => !preparedTargets.has(target));
52795
+ }
52796
+ filterProtocolV2LocalInstallItems(installItems) {
52797
+ var _a;
52798
+ if (!this.protocolV2HasExplicitTargetSelection) {
52799
+ return installItems;
52800
+ }
52801
+ const requestedTargets = new Set((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : []);
52802
+ return installItems.filter(item => {
52803
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52804
+ return target !== undefined && requestedTargets.has(target);
52805
+ });
51876
52806
  }
51877
52807
  buildProtocolV2InstallItems({ bootloaderBinary, fwBinaryMap, }) {
51878
52808
  const installItems = [];
@@ -51921,7 +52851,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51921
52851
  if (!component.url) {
51922
52852
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Missing Protocol V2 firmware component url: ${key}/${component.target}`);
51923
52853
  }
52854
+ const expectedFingerprint = normalizeProtocolV2Hex(component.fingerprint);
52855
+ if (!Number.isSafeInteger(component.expectedSize) ||
52856
+ Number(component.expectedSize) <= 0 ||
52857
+ !expectedFingerprint ||
52858
+ !/^[0-9a-f]{64}$/u.test(expectedFingerprint)) {
52859
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware component integrity metadata is invalid: ${key}/${component.target}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
52860
+ }
51924
52861
  const { binary } = yield getSysResourceBinary(component.url);
52862
+ if (binary.byteLength !== component.expectedSize) {
52863
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware size mismatch: ${key}/${component.target}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52864
+ }
51925
52865
  if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
51926
52866
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`);
51927
52867
  }
@@ -51935,7 +52875,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51935
52875
  return Object.assign(Object.assign({}, target), { binary });
51936
52876
  });
51937
52877
  }
51938
- prepareRemoteProtocolV2Binaries(firmwareType, features) {
52878
+ prepareRemoteProtocolV2Binaries(firmwareType, features, explicitInstallItems = []) {
51939
52879
  var _a, _b;
51940
52880
  return __awaiter(this, void 0, void 0, function* () {
51941
52881
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
@@ -51943,6 +52883,10 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51943
52883
  const fwBinaryMap = [];
51944
52884
  const installItems = [];
51945
52885
  if (!release) {
52886
+ const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
52887
+ if (missingFirmwareTargets.length > 0) {
52888
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware release is unavailable for requested targets: ${missingFirmwareTargets.join(', ')}`);
52889
+ }
51946
52890
  return {
51947
52891
  bootloaderBinary,
51948
52892
  fwBinaryMap,
@@ -51951,6 +52895,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51951
52895
  }
51952
52896
  const entries = this.getRemoteComponentEntries(release);
51953
52897
  const targetsToUpdate = new Set((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : []);
52898
+ const explicitInstallItemByTargetId = new Map(explicitInstallItems.map(item => [item.targetId, item]));
52899
+ const preparedTargets = new Set();
51954
52900
  for (const [key, component] of entries) {
51955
52901
  const targetName = (_b = component.target) === null || _b === void 0 ? void 0 : _b.toUpperCase();
51956
52902
  const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
@@ -51958,27 +52904,27 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51958
52904
  ? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
51959
52905
  : undefined;
51960
52906
  if (updateTarget && targetsToUpdate.has(updateTarget)) {
51961
- const remoteBinary = yield this.downloadRemoteProtocolV2Component(key, component);
51962
- if (remoteBinary.kind === 'bootloader') {
51963
- bootloaderBinary = remoteBinary.binary;
51964
- installItems.push({
51965
- fileName: remoteBinary.fileName,
51966
- binary: remoteBinary.binary,
51967
- targetId: remoteBinary.targetId,
51968
- kind: remoteBinary.kind,
51969
- });
52907
+ const explicitInstallItem = explicitInstallItemByTargetId.get(target.targetId);
52908
+ const installItem = explicitInstallItem !== null && explicitInstallItem !== void 0 ? explicitInstallItem : (yield this.downloadRemoteProtocolV2Component(key, component));
52909
+ if (installItem.kind === 'bootloader') {
52910
+ bootloaderBinary = installItem.binary;
51970
52911
  }
51971
52912
  else {
51972
52913
  const binaryEntry = {
51973
- fileName: remoteBinary.fileName,
51974
- binary: remoteBinary.binary,
51975
- targetId: remoteBinary.targetId,
52914
+ fileName: installItem.fileName,
52915
+ binary: installItem.binary,
52916
+ targetId: installItem.targetId,
51976
52917
  };
51977
52918
  fwBinaryMap.push(binaryEntry);
51978
- installItems.push(Object.assign(Object.assign({}, binaryEntry), { kind: remoteBinary.kind }));
51979
52919
  }
52920
+ installItems.push(Object.assign({}, installItem));
52921
+ preparedTargets.add(updateTarget);
51980
52922
  }
51981
52923
  }
52924
+ const missingTarget = Array.from(targetsToUpdate).find(target => target !== 'resource' && !preparedTargets.has(target));
52925
+ if (missingTarget) {
52926
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 release does not contain requested target ${missingTarget}`);
52927
+ }
51982
52928
  return {
51983
52929
  bootloaderBinary,
51984
52930
  fwBinaryMap,
@@ -51986,178 +52932,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51986
52932
  };
51987
52933
  });
51988
52934
  }
51989
- getProtocolV2DeviceType() {
51990
- const deviceType = this.device.getCurrentDeviceType();
51991
- if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo)
51992
- return deviceType;
51993
- throw new Error(`Unsupported Protocol V2 device type: ${deviceType}`);
51994
- }
51995
- prepareProtocolV2BootResources() {
51996
- var _a, _b, _c, _d, _e;
51997
- return __awaiter(this, void 0, void 0, function* () {
51998
- const wantsStableResources = !!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'));
51999
- const wantsBootResources = !!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('boot_resources'));
52000
- if (!wantsStableResources && !wantsBootResources) {
52001
- return undefined;
52002
- }
52003
- if ((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length) {
52004
- return undefined;
52005
- }
52006
- const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
52007
- if (!resource) {
52008
- if (wantsBootResources) {
52009
- throw new Error('Missing Protocol V2 boot resources configuration');
52010
- }
52011
- Log$6.debug('[FirmwareUpdateV4] no boot resources configured; continue with stable resources');
52012
- return undefined;
52013
- }
52014
- const files = [];
52015
- for (const file of resource.files) {
52016
- const isCurrent = !this.params.forcedUpdateRes && (yield this.isProtocolV2BootResourceCurrent(file));
52017
- if (isCurrent) {
52018
- Log$6.log(`[FirmwareUpdateV4] boot resource unchanged, skipping ${file.devicePath}`);
52019
- }
52020
- else {
52021
- Log$6.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
52022
- const { binary } = yield getSysResourceBinary(file.url);
52023
- if (!isProtocolV2ResourceFileValid(binary, file)) {
52024
- throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
52025
- }
52026
- files.push({
52027
- name: (_e = (_d = file.name) !== null && _d !== void 0 ? _d : file.devicePath.split('/').pop()) !== null && _e !== void 0 ? _e : file.devicePath,
52028
- binary,
52029
- devicePath: file.devicePath,
52030
- });
52031
- }
52032
- }
52033
- return files;
52034
- });
52035
- }
52036
- isProtocolV2BootResourceCurrent(file) {
52037
- var _a, _b, _c, _d;
52038
- return __awaiter(this, void 0, void 0, function* () {
52039
- try {
52040
- const commands = this.device.getCommands();
52041
- const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path: file.devicePath }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
52042
- const size = toProtocolV2FiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
52043
- if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
52044
- ((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
52045
- !Number.isSafeInteger(size) ||
52046
- size !== file.size) {
52047
- return false;
52048
- }
52049
- const digest = sha256.sha256.create();
52050
- const chunkSize = this.getProtocolV2FirmwareChunkSize('write');
52051
- let offset = 0;
52052
- while (offset < file.size) {
52053
- const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
52054
- file: { path: file.devicePath, offset, total_size: 0 },
52055
- chunk_len: Math.min(chunkSize, file.size - offset),
52056
- }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
52057
- const data = toProtocolV2Bytes((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
52058
- if (data.byteLength === 0)
52059
- return false;
52060
- const consumed = data.subarray(0, Math.min(data.byteLength, file.size - offset));
52061
- digest.update(consumed);
52062
- offset += consumed.byteLength;
52063
- }
52064
- return bytesToHex(digest.digest()) === normalizeProtocolV2Hex(file.fileHash);
52065
- }
52066
- catch (error) {
52067
- Log$6.debug(`[FirmwareUpdateV4] unable to compare boot resource ${file.devicePath}; scheduling rewrite`, error);
52068
- return false;
52069
- }
52070
- });
52071
- }
52072
- mergeProtocolV2ResourceBundles(...groups) {
52073
- const merged = groups.flatMap(group => group !== null && group !== void 0 ? group : []);
52074
- if (!merged.length)
52075
- return undefined;
52076
- const seenPaths = new Set();
52077
- for (const bundle of merged) {
52078
- if (seenPaths.has(bundle.devicePath)) {
52079
- throw new Error(`Duplicate Protocol V2 resource devicePath: ${bundle.devicePath}`);
52080
- }
52081
- seenPaths.add(bundle.devicePath);
52082
- }
52083
- return merged;
52084
- }
52085
- prepareExplicitProtocolV2ResourceFiles() {
52086
- var _a;
52087
- const files = (_a = this.params.resourceFiles) !== null && _a !== void 0 ? _a : [];
52088
- if (!(files === null || files === void 0 ? void 0 : files.length))
52089
- return undefined;
52090
- const prepared = files.map((file, index) => {
52091
- var _a;
52092
- const devicePath = validateProtocolV2FilesystemPath(file.devicePath, `resourceFiles[${index}].devicePath`);
52093
- const descriptor = file;
52094
- if (descriptor.size !== undefined && descriptor.size !== file.binary.byteLength) {
52095
- throw new Error(`resourceFiles[${index}] size mismatch`);
52096
- }
52097
- if (descriptor.fileHash &&
52098
- !isProtocolV2ResourceFileValid(file.binary, {
52099
- size: file.binary.byteLength,
52100
- fileHash: descriptor.fileHash,
52101
- })) {
52102
- throw new Error(`resourceFiles[${index}] SHA-256 mismatch`);
52103
- }
52104
- return {
52105
- name: (_a = devicePath.split('/').pop()) !== null && _a !== void 0 ? _a : devicePath,
52106
- binary: file.binary,
52107
- devicePath,
52108
- };
52109
- });
52110
- if (new Set(prepared.map(file => file.devicePath)).size !== prepared.length) {
52111
- throw new Error('resourceFiles contain duplicate devicePath values');
52112
- }
52113
- return prepared;
52114
- }
52115
- prepareProtocolV2ResourceBundles() {
52116
- var _a;
52117
- return __awaiter(this, void 0, void 0, function* () {
52118
- if (!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'))) {
52119
- return undefined;
52120
- }
52121
- const resources = DataManager.getProtocolV2Resources(this.getProtocolV2DeviceType());
52122
- if (!(resources === null || resources === void 0 ? void 0 : resources.length)) {
52123
- throw new Error('Missing Pro2 stable resource configuration');
52124
- }
52125
- const inventory = this.params.forcedUpdateRes
52126
- ? undefined
52127
- : yield readProtocolV2ResourceInventory({
52128
- commands: this.device.getCommands(),
52129
- resources,
52130
- chunkSize: this.getProtocolV2FirmwareChunkSize('write'),
52131
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
52132
- });
52133
- const plan = buildProtocolV2ResourceUpdatePlan({
52134
- resources,
52135
- inventory,
52136
- mode: 'bootloader-recovery',
52137
- forced: this.params.forcedUpdateRes,
52138
- });
52139
- Log$6.log(`[FirmwareUpdateV4] Protocol V2 resource plan mode=bootloader-recovery status=${plan.status} count=${plan.resources.length}`);
52140
- const bundles = [];
52141
- for (const resource of plan.resources) {
52142
- bundles.push(yield this.downloadProtocolV2Resource(resource));
52143
- }
52144
- return bundles;
52145
- });
52146
- }
52147
- downloadProtocolV2Resource(resource) {
52148
- return __awaiter(this, void 0, void 0, function* () {
52149
- Log$6.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
52150
- const { binary } = yield getSysResourceBinary(resource.url);
52151
- if (!isProtocolV2ResourceFileValid(binary, resource)) {
52152
- throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
52153
- }
52154
- return {
52155
- name: `${resource.type}.okpkg`,
52156
- binary,
52157
- devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
52158
- };
52159
- });
52160
- }
52161
52935
  getProtocolV2ResourceFilePath(path) {
52162
52936
  if (path.startsWith('vol'))
52163
52937
  return path;
@@ -52165,7 +52939,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52165
52939
  return `vol0:${path}`;
52166
52940
  return `vol0:/${path}`;
52167
52941
  }
52168
- readProtocolV2DeviceFileHeader(path) {
52942
+ readProtocolV2DeviceFileHeader(path, expectedSize) {
52169
52943
  var _a, _b, _c, _d;
52170
52944
  return __awaiter(this, void 0, void 0, function* () {
52171
52945
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
@@ -52177,7 +52951,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52177
52951
  if (!((_b = pathInfoRes.message) === null || _b === void 0 ? void 0 : _b.exist) ||
52178
52952
  ((_c = pathInfoRes.message) === null || _c === void 0 ? void 0 : _c.directory) ||
52179
52953
  fileSize === undefined ||
52180
- fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE) {
52954
+ fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE ||
52955
+ (expectedSize !== undefined && fileSize !== expectedSize)) {
52181
52956
  return null;
52182
52957
  }
52183
52958
  const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
@@ -52214,10 +52989,10 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52214
52989
  return __awaiter(this, void 0, void 0, function* () {
52215
52990
  if ((_a = this.params) === null || _a === void 0 ? void 0 : _a.forcedUpdateRes)
52216
52991
  return false;
52217
- if (!bundle.version && !bundle.payloadHash)
52992
+ if (!bundle.payloadHash || !bundle.headerHash)
52218
52993
  return false;
52219
52994
  try {
52220
- const header = yield this.readProtocolV2DeviceFileHeader(bundle.devicePath);
52995
+ const header = yield this.readProtocolV2DeviceFileHeader(bundle.devicePath, bundle.source.size);
52221
52996
  if (!header)
52222
52997
  return false;
52223
52998
  if (bundle.version) {
@@ -52225,16 +53000,12 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52225
53000
  if (cmp === undefined || cmp !== 0)
52226
53001
  return false;
52227
53002
  }
52228
- if (bundle.payloadHash) {
52229
- const expected = normalizeProtocolV2Hex(bundle.payloadHash);
52230
- if (expected && header.payloadHash !== expected)
52231
- return false;
52232
- }
52233
- if (bundle.headerHash) {
52234
- const expected = normalizeProtocolV2Hex(bundle.headerHash);
52235
- if (expected && header.headerHash !== expected)
52236
- return false;
52237
- }
53003
+ const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
53004
+ const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
53005
+ if (!expectedPayloadHash || header.payloadHash !== expectedPayloadHash)
53006
+ return false;
53007
+ if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash)
53008
+ return false;
52238
53009
  return true;
52239
53010
  }
52240
53011
  catch (error) {
@@ -52349,11 +53120,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52349
53120
  }
52350
53121
  buildProtocolV2ExecutionPhases({ installSources, resourceSources, }) {
52351
53122
  const phases = [];
52352
- if (resourceSources.length > 0) {
53123
+ const bootResourceSources = resourceSources.filter(source => isProtocolV2BootResourcePackagePath(source.devicePath));
53124
+ const remainingResourceSources = resourceSources.filter(source => !isProtocolV2BootResourcePackagePath(source.devicePath));
53125
+ if (bootResourceSources.length > 0) {
52353
53126
  phases.push({
52354
53127
  kind: 'resource-sync',
52355
53128
  installSources: [],
52356
- resourceSources,
53129
+ resourceSources: bootResourceSources,
52357
53130
  });
52358
53131
  }
52359
53132
  const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
@@ -52368,6 +53141,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52368
53141
  resourceSources: [],
52369
53142
  });
52370
53143
  }
53144
+ if (remainingResourceSources.length > 0) {
53145
+ phases.push({
53146
+ kind: 'resource-sync',
53147
+ installSources: [],
53148
+ resourceSources: remainingResourceSources,
53149
+ });
53150
+ }
52371
53151
  const componentSources = installSources.filter(source => source.kind !== 'bootloader');
52372
53152
  if (componentSources.length > 0) {
52373
53153
  phases.push({
@@ -52385,6 +53165,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52385
53165
  }
52386
53166
  executeProtocolV2Phases({ installSources, resourceSources, }) {
52387
53167
  return __awaiter(this, void 0, void 0, function* () {
53168
+ this.protocolV2BootResourceStagingSafe = false;
52388
53169
  const phases = this.buildProtocolV2ExecutionPhases({
52389
53170
  installSources,
52390
53171
  resourceSources,
@@ -52395,10 +53176,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52395
53176
  }
52396
53177
  if (phase.kind === 'resource-sync') {
52397
53178
  yield this.enterProtocolV2BootloaderMode();
53179
+ if (!phase.resourceSources.some(source => isProtocolV2BootResourcePackagePath(source.devicePath))) {
53180
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
53181
+ }
52398
53182
  yield this.executeProtocolV2TransferPhase(phase);
52399
53183
  }
52400
53184
  else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
52401
53185
  yield this.enterProtocolV2BootloaderMode();
53186
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
52402
53187
  yield this.executeProtocolV2TransferPhase(phase);
52403
53188
  yield this.exitProtocolV2BootloaderToNormal();
52404
53189
  }
@@ -52410,6 +53195,31 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52410
53195
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 execution has no final verification phase');
52411
53196
  });
52412
53197
  }
53198
+ ensureProtocolV2BootResourceStagingIsEmpty() {
53199
+ var _a, _b;
53200
+ return __awaiter(this, void 0, void 0, function* () {
53201
+ if (this.protocolV2BootResourceStagingSafe)
53202
+ return;
53203
+ const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
53204
+ const query = () => typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
53205
+ path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
53206
+ });
53207
+ const current = yield query();
53208
+ if ((_a = current.message) === null || _a === void 0 ? void 0 : _a.exist) {
53209
+ if (current.message.directory) {
53210
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, 'Protocol V2 boot resource staging path is not a file');
53211
+ }
53212
+ yield typedCall('FilesystemFileDelete', 'Success', {
53213
+ path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
53214
+ });
53215
+ const remaining = yield query();
53216
+ if ((_b = remaining.message) === null || _b === void 0 ? void 0 : _b.exist) {
53217
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, 'Protocol V2 stale boot resource staging file could not be removed');
53218
+ }
53219
+ }
53220
+ this.protocolV2BootResourceStagingSafe = true;
53221
+ });
53222
+ }
52413
53223
  executeProtocolV2SourceUpdate({ installSources, resourceSources, }) {
52414
53224
  return __awaiter(this, void 0, void 0, function* () {
52415
53225
  return this.executeProtocolV2Phases({
@@ -52418,7 +53228,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52418
53228
  });
52419
53229
  });
52420
53230
  }
52421
- executeProtocolV2Update({ fwBinaryMap, bootloaderBinary, installItems, resourceBundles, }) {
53231
+ executeProtocolV2Update({ fwBinaryMap, bootloaderBinary, installItems, }) {
52422
53232
  return __awaiter(this, void 0, void 0, function* () {
52423
53233
  const memoryInstallItems = installItems !== null && installItems !== void 0 ? installItems : this.buildProtocolV2InstallItems({
52424
53234
  fwBinaryMap: fwBinaryMap !== null && fwBinaryMap !== void 0 ? fwBinaryMap : [],
@@ -52433,16 +53243,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52433
53243
  kind: item.kind,
52434
53244
  });
52435
53245
  })));
52436
- const resourceSources = yield Promise.all((resourceBundles !== null && resourceBundles !== void 0 ? resourceBundles : []).map((bundle) => __awaiter(this, void 0, void 0, function* () {
52437
- return ({
52438
- name: bundle.name,
52439
- source: yield this.openProtocolV2MemorySource(bundle.binary),
52440
- devicePath: bundle.devicePath,
52441
- });
52442
- })));
52443
53246
  return yield this.executeProtocolV2Phases({
52444
53247
  installSources,
52445
- resourceSources,
53248
+ resourceSources: [],
52446
53249
  });
52447
53250
  }
52448
53251
  finally {
@@ -52455,7 +53258,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52455
53258
  let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
52456
53259
  const resourcesToSync = [];
52457
53260
  for (const resource of resourceSources) {
52458
- if (yield this.isProtocolV2ResourceBundleUpToDate(resource)) {
53261
+ const requiresFreshStaging = isProtocolV2BootResourcePackagePath(resource.devicePath);
53262
+ if (!requiresFreshStaging && (yield this.isProtocolV2ResourceBundleUpToDate(resource))) {
52459
53263
  Log$6.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
52460
53264
  }
52461
53265
  else {
@@ -52466,13 +53270,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52466
53270
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartTransferData);
52467
53271
  let processedSize = 0;
52468
53272
  for (const resource of resourcesToSync) {
53273
+ const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
52469
53274
  processedSize = yield this.protocolV2SourceUpdateProcess({
52470
53275
  source: resource.source,
52471
- filePath: resource.devicePath,
53276
+ filePath: writePath,
52472
53277
  processedSize,
52473
53278
  totalSize,
52474
53279
  });
52475
- yield this.verifyProtocolV2StagedFile(resource.devicePath, resource.source.size);
53280
+ yield this.verifyProtocolV2StagedFile(writePath, resource.source.size);
53281
+ if (isProtocolV2BootResourcePackagePath(resource.devicePath)) {
53282
+ this.protocolV2BootResourceStagingSafe = true;
53283
+ }
52476
53284
  }
52477
53285
  const stagedInstallTargets = [];
52478
53286
  for (const item of installSources) {
@@ -53462,7 +54270,7 @@ class DeviceUploadWallpaper extends BaseMethod {
53462
54270
  }
53463
54271
 
53464
54272
  const FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE = 60802;
53465
- const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
54273
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1 = 60805;
53466
54274
  const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
53467
54275
  const NFT_UPDATE_MESSAGE_TYPE = 61500;
53468
54276
  class DeviceUploadNft extends BaseMethod {
@@ -53491,7 +54299,7 @@ class DeviceUploadNft extends BaseMethod {
53491
54299
  assertCapabilities() {
53492
54300
  return __awaiter(this, void 0, void 0, function* () {
53493
54301
  const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
53494
- const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
54302
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1);
53495
54303
  const hasPathInfo = supportsProtocolV2Message(protocolInfo, FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE);
53496
54304
  const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
53497
54305
  const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
@@ -53626,6 +54434,8 @@ class FileWrite extends BaseMethod {
53626
54434
 
53627
54435
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
53628
54436
  const PORTFOLIO_CHUNK_SIZE = 2048;
54437
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
54438
+ const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
53629
54439
  class UploadPortfolio extends FileWrite {
53630
54440
  init() {
53631
54441
  const { packageBytes, timeoutMs } = this.payload;
@@ -53639,6 +54449,12 @@ class UploadPortfolio extends FileWrite {
53639
54449
  run: { get: () => super.run }
53640
54450
  });
53641
54451
  return __awaiter(this, void 0, void 0, function* () {
54452
+ const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
54453
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
54454
+ const hasPortfolioUpdate = supportsProtocolV2Message(protocolInfo, PORTFOLIO_UPDATE_MESSAGE_TYPE);
54455
+ if (!hasFileWrite || !hasPortfolioUpdate) {
54456
+ throw hdShared.createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
54457
+ }
53642
54458
  const stagedFile = yield _super.run.call(this);
53643
54459
  this.throwIfAborted();
53644
54460
  yield this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
@@ -65180,11 +65996,14 @@ exports.normalizeSafetyCheckLevel = normalizeSafetyCheckLevel;
65180
65996
  exports.normalizeVersionArray = normalizeVersionArray;
65181
65997
  exports.parseConnectSettings = parseConnectSettings;
65182
65998
  exports.parseMessage = parseMessage;
65999
+ exports.parseProtocolV2ResourceManifest = parseProtocolV2ResourceManifest;
65183
66000
  exports.patchFeatures = patchFeatures;
65184
66001
  exports.preloadSessionCache = preloadSessionCache;
66002
+ exports.prepareFirmwareUpdateV4MemoryHost = prepareFirmwareUpdateV4MemoryHost;
65185
66003
  exports.projectDeviceStateFeatures = projectFeatures;
65186
66004
  exports.registerFirmwareUpdateHostBinding = registerFirmwareUpdateHostBinding;
65187
66005
  exports.safeThrowError = safeThrowError;
66006
+ exports.selectProtocolV2ResourceManifestFiles = selectProtocolV2ResourceManifestFiles;
65188
66007
  exports.setLoggerPostMessage = setLoggerPostMessage;
65189
66008
  exports.supportInputPinOnSoftware = supportInputPinOnSoftware;
65190
66009
  exports.switchTransport = switchTransport;