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

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 (98) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +404 -84
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  3. package/__tests__/firmware-memory-host.test.ts +112 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +13 -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-plan.test.ts +232 -70
  9. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  10. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  11. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  12. package/__tests__/protocol-v2-resources.test.ts +148 -232
  13. package/__tests__/protocol-v2.test.ts +1236 -516
  14. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
  15. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  16. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  17. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  18. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  19. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  20. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  21. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  22. package/dist/api/FirmwareUpdateV2.d.ts +1 -0
  23. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  24. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  25. package/dist/api/FirmwareUpdateV4.d.ts +8 -8
  26. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  27. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  28. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  29. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  30. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  31. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  32. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  33. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  34. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  35. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
  36. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  37. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +6 -1
  38. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  39. package/dist/api/firmware/getBinary.d.ts +0 -1
  40. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  41. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  42. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  43. package/dist/data-manager/DataManager.d.ts +4 -3
  44. package/dist/data-manager/DataManager.d.ts.map +1 -1
  45. package/dist/index.d.ts +108 -114
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1633 -967
  48. package/dist/protocols/protocol-v2/resources.d.ts +10 -28
  49. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  50. package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
  51. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  52. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  53. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  54. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  55. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  56. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  57. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  58. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  59. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  60. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  61. package/dist/types/api/export.d.ts +1 -1
  62. package/dist/types/api/export.d.ts.map +1 -1
  63. package/dist/types/api/firmwareUpdate.d.ts +4 -12
  64. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  65. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  66. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  67. package/dist/types/settings.d.ts +35 -36
  68. package/dist/types/settings.d.ts.map +1 -1
  69. package/package.json +4 -4
  70. package/src/api/CheckAllFirmwareRelease.ts +108 -80
  71. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  72. package/src/api/CheckBootloaderRelease.ts +35 -3
  73. package/src/api/CheckFirmwareRelease.ts +35 -7
  74. package/src/api/FirmwareUpdateV2.ts +108 -73
  75. package/src/api/FirmwareUpdateV3.ts +101 -56
  76. package/src/api/FirmwareUpdateV4.ts +885 -403
  77. package/src/api/UploadPortfolio.ts +18 -0
  78. package/src/api/device/DeviceUpdateBootloader.ts +37 -11
  79. package/src/api/firmware/FirmwareHostBinding.ts +16 -3
  80. package/src/api/firmware/FirmwareMemoryHost.ts +142 -0
  81. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  82. package/src/api/firmware/FirmwareUpdatePlan.ts +282 -122
  83. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +27 -3
  84. package/src/api/firmware/protocolV2Release.ts +168 -0
  85. package/src/data-manager/DataManager.ts +35 -19
  86. package/src/index.ts +10 -0
  87. package/src/protocols/protocol-v2/resources.ts +157 -341
  88. package/src/types/api/checkAllFirmwareRelease.ts +27 -12
  89. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  90. package/src/types/api/checkBootloaderRelease.ts +2 -6
  91. package/src/types/api/checkFirmwareRelease.ts +2 -13
  92. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  93. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  94. package/src/types/api/export.ts +6 -1
  95. package/src/types/api/firmwareUpdate.ts +12 -17
  96. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  97. package/src/types/settings.ts +35 -61
  98. 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,18 @@ 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
+ value.length > PROTOCOL_V2_FIRMWARE_UPDATE_TARGETS.size ||
632
+ value.some(target => !PROTOCOL_V2_FIRMWARE_UPDATE_TARGETS.has(target)) ||
633
+ new Set(value).size !== value.length) {
634
+ return planError('Protocol V2 firmware update targets are invalid');
635
+ }
636
+ return [...value];
637
+ };
612
638
  const assertExactKeys = (value, required, optional = []) => {
613
639
  const allowed = new Set([...required, ...optional]);
614
640
  if (required.some(key => !Object.prototype.hasOwnProperty.call(value, key)) ||
@@ -689,7 +715,7 @@ const assertFirmwareUpdatePlan = (value) => {
689
715
  if (!artifact) {
690
716
  return planError('Firmware update plan artifact is invalid');
691
717
  }
692
- assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container'], ['logicalName', 'expectedSize', 'expectedSha256', 'targetVersion']);
718
+ assertExactKeys(artifact, ['artifactId', 'role', 'target', 'url', 'container', 'expectedSize', 'expectedSha256'], ['logicalName', 'targetVersion']);
693
719
  const artifactId = assertBoundedString(artifact.artifactId, 'artifact id', 160);
694
720
  if (artifactIds.has(artifactId)) {
695
721
  return planError('Firmware update plan contains duplicate artifact ids');
@@ -705,13 +731,11 @@ const assertFirmwareUpdatePlan = (value) => {
705
731
  if (artifact.logicalName !== undefined) {
706
732
  assertBoundedString(artifact.logicalName, 'logical name', 256);
707
733
  }
708
- if (artifact.expectedSize !== undefined &&
709
- (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0)) {
734
+ if (!Number.isSafeInteger(artifact.expectedSize) || artifact.expectedSize <= 0) {
710
735
  return planError('Firmware update plan artifact size is invalid');
711
736
  }
712
- if (artifact.expectedSha256 !== undefined &&
713
- (typeof artifact.expectedSha256 !== 'string' ||
714
- !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256))) {
737
+ if (typeof artifact.expectedSha256 !== 'string' ||
738
+ !/^[a-f0-9]{64}$/u.test(artifact.expectedSha256)) {
715
739
  return planError('Firmware update plan artifact digest is invalid');
716
740
  }
717
741
  if (artifact.targetVersion !== undefined) {
@@ -738,7 +762,7 @@ const assertFirmwareUpdatePlan = (value) => {
738
762
  plan.targetsToUpdate.some(target => legacyOnlyTargets.has(target))) ||
739
763
  (plan.artifacts.length > 0 &&
740
764
  (plan.platform === 'native' || plan.platform === 'desktop') &&
741
- plan.executor !== 'v2' &&
765
+ plan.executor === 'v3' &&
742
766
  plan.deviceIdentity === 'unavailable')) {
743
767
  return planError('Firmware update plan executor contract is invalid');
744
768
  }
@@ -773,7 +797,7 @@ const selectResourceUrl = ({ release, features, platform, }) => {
773
797
  };
774
798
  const getExecutor = (features) => {
775
799
  const deviceType = getDeviceType(features);
776
- if (deviceType === hdShared.EDeviceType.Pro2) {
800
+ if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo) {
777
801
  return 'v4';
778
802
  }
779
803
  if (deviceType === hdShared.EDeviceType.Pro &&
@@ -782,7 +806,7 @@ const getExecutor = (features) => {
782
806
  }
783
807
  return 'v2';
784
808
  };
785
- const buildProtocolV2Artifacts = (release, { includeComponents = true, includeResources = true, } = {}) => {
809
+ const buildProtocolV2Artifacts = (release, { includeComponents = true, componentTargets: selectedComponentTargets, } = {}) => {
786
810
  var _a;
787
811
  const components = asRecord(release.components);
788
812
  if (includeComponents && !components) {
@@ -794,49 +818,36 @@ const buildProtocolV2Artifacts = (release, { includeComponents = true, includeRe
794
818
  const componentKeys = includeComponents && components
795
819
  ? [...installOrder, ...Object.keys(components).filter(key => !installOrder.includes(key))]
796
820
  : [];
821
+ const selectedComponentKeys = selectedComponentTargets
822
+ ? componentKeys.filter(key => {
823
+ var _a;
824
+ const component = asRecord(components === null || components === void 0 ? void 0 : components[key]);
825
+ const targetName = (_a = asString(component === null || component === void 0 ? void 0 : component.target)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
826
+ const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
827
+ return !!target && selectedComponentTargets.has(target);
828
+ })
829
+ : componentKeys;
797
830
  const artifacts = [];
798
831
  const targets = [];
799
- const componentTargets = new Set();
800
- for (const key of componentKeys) {
832
+ const componentTargetSet = new Set();
833
+ for (const key of selectedComponentKeys) {
801
834
  const component = asRecord(components === null || components === void 0 ? void 0 : components[key]);
802
835
  const targetName = (_a = asString(component === null || component === void 0 ? void 0 : component.target)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
836
+ const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
803
837
  if (targetName === 'ROMLOADER') {
804
838
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 ROMLOADER requires its dedicated loader flow', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
805
839
  }
806
- const target = targetName ? PROTOCOL_V2_TARGETS[targetName] : undefined;
807
840
  if (!component || !target) {
808
841
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 component ${key} has an unsupported target`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
809
842
  }
810
- if (componentTargets.has(target)) {
843
+ if (componentTargetSet.has(target)) {
811
844
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 component ${key} duplicates target ${target}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
812
845
  }
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) } : {})));
846
+ componentTargetSet.add(target);
847
+ const integrity = requireArtifactIntegrity({ size: component.expectedSize, sha256: component.fingerprint }, `Protocol V2 component ${key}`);
848
+ 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
849
  targets.push(target);
819
850
  }
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
851
  return { artifacts, targets: [...new Set(targets)] };
841
852
  };
842
853
  const isForcedTargetRepresented = ({ executor, forcedTarget, artifacts, targetsToUpdate, }) => {
@@ -865,8 +876,91 @@ const assertForcedTargetsRepresented = ({ executor, forcedTargets, artifacts, ta
865
876
  }
866
877
  }
867
878
  };
879
+ const finalizeFirmwareUpdatePlan = ({ features, firmwareType, platform, artifacts, targetsToUpdate, }) => {
880
+ const executor = getExecutor(features);
881
+ const bootloaderMode = resolveDeviceBootloaderMode(features);
882
+ const reportedIdentity = getDeviceUUID(features);
883
+ const deviceIdentity = reportedIdentity === 'unavailable' ? '' : reportedIdentity;
884
+ const allowsUnavailableIdentity = (bootloaderMode && executor === 'v2') || executor === 'v4';
885
+ if (artifacts.length > 0 &&
886
+ (platform === 'native' || platform === 'desktop') &&
887
+ !deviceIdentity &&
888
+ !allowsUnavailableIdentity) {
889
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware update requires a stable device identity', { firmwareUpdateCode: 'FirmwarePlanInvalid' });
890
+ }
891
+ const planWithoutDigest = {
892
+ schemaVersion: 2,
893
+ executor,
894
+ deviceIdentity: deviceIdentity || 'unavailable',
895
+ deviceModel: String(getDeviceType(features)),
896
+ firmwareType,
897
+ platform,
898
+ artifacts,
899
+ targetsToUpdate,
900
+ };
901
+ return assertFirmwareUpdatePlan(Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) }));
902
+ };
903
+ const buildProtocolV2LocalFirmwareUpdatePlan = ({ features, firmwareType, platform, artifacts, }) => {
904
+ if (artifacts.length === 0) {
905
+ return planError('Protocol V2 local firmware plan has no artifacts');
906
+ }
907
+ const plan = finalizeFirmwareUpdatePlan({
908
+ features,
909
+ firmwareType,
910
+ platform,
911
+ artifacts: artifacts.map(artifact => (Object.assign(Object.assign({}, artifact), { role: artifact.target === 'resource' ? 'resourceBundle' : 'component', url: `https://local-firmware.invalid/${encodeURIComponent(artifact.artifactId)}` }))),
912
+ targetsToUpdate: artifacts.map(artifact => artifact.target),
913
+ });
914
+ if (plan.executor !== 'v4') {
915
+ return planError('Protocol V2 local firmware plan requires executor v4');
916
+ }
917
+ return plan;
918
+ };
919
+ const buildProtocolV2FirmwareUpdatePlan = ({ features, firmwareType, platform, release, targetsToUpdate, forceUpdateTargets, resourceArchive, }) => {
920
+ const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
921
+ if (validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
922
+ planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
923
+ }
924
+ const requestedComponentTargets = new Set(targetsToUpdate.filter(target => target !== 'resource'));
925
+ if (getDeviceType(features) === hdShared.EDeviceType.Neo &&
926
+ (requestedComponentTargets.has('se03') || requestedComponentTargets.has('se04'))) {
927
+ planError('Neo does not support Protocol V2 targets se03 or se04');
928
+ }
929
+ const protocolV2 = buildProtocolV2Artifacts(release !== null && release !== void 0 ? release : {}, {
930
+ includeComponents: requestedComponentTargets.size > 0,
931
+ componentTargets: requestedComponentTargets,
932
+ });
933
+ const resourceRequested = targetsToUpdate.includes('resource');
934
+ if (resourceRequested) {
935
+ const archive = resourceArchive;
936
+ if (!archive) {
937
+ return planError('Protocol V2 resource target has no resource archive');
938
+ }
939
+ const integrity = requireArtifactIntegrity({ size: archive.archiveSize, sha256: archive.archiveSha256 }, 'Protocol V2 resource archive');
940
+ 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));
941
+ protocolV2.targets.push('resource');
942
+ }
943
+ const representedTargets = new Set(protocolV2.targets);
944
+ const missingTarget = Array.from(requestedComponentTargets).find(target => !representedTargets.has(target));
945
+ if (missingTarget) {
946
+ planError(`Protocol V2 release does not contain requested target ${missingTarget}`);
947
+ }
948
+ if (validatedForceTargets.includes('firmware') && protocolV2.targets.length === 0) {
949
+ planError('Forced firmware update target firmware is not represented by the plan');
950
+ }
951
+ if (validatedForceTargets.includes('resource') && !resourceArchive) {
952
+ planError('Forced firmware update target resource has no Protocol V2 resource archive');
953
+ }
954
+ return finalizeFirmwareUpdatePlan({
955
+ features,
956
+ firmwareType,
957
+ platform,
958
+ artifacts: protocolV2.artifacts,
959
+ targetsToUpdate: protocolV2.targets,
960
+ });
961
+ };
868
962
  const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, ble, bootloader, forceUpdateTargets, }) => {
869
- var _a, _b, _c, _d, _e, _f, _g;
963
+ var _a, _b;
870
964
  const executor = getExecutor(features);
871
965
  let artifacts = [];
872
966
  let targetsToUpdate = [];
@@ -878,7 +972,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
878
972
  ((release === null || release === void 0 ? void 0 : release.status) === 'none' || (release === null || release === void 0 ? void 0 : release.status) === 'unknown') &&
879
973
  !!asRelease(release);
880
974
  const shouldUpdateFirmware = isUpgrade(firmware) || isRecoveryInstall(firmware) || forcedTargets.has('firmware');
881
- const shouldUpdateResource = isUpgrade(firmware) || forcedTargets.has('resource');
975
+ const shouldUpdateResource = !(executor === 'v2' && bootloaderMode) &&
976
+ (isUpgrade(firmware) || forcedTargets.has('resource'));
882
977
  if (executor === 'v4' &&
883
978
  validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
884
979
  planError('Protocol V2 does not support forced BLE or legacy bootloader targets');
@@ -886,19 +981,20 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
886
981
  if (executor === 'v4' && (shouldUpdateFirmware || shouldUpdateResource)) {
887
982
  const protocolV2 = buildProtocolV2Artifacts(firmwareRelease !== null && firmwareRelease !== void 0 ? firmwareRelease : {}, {
888
983
  includeComponents: shouldUpdateFirmware,
889
- includeResources: shouldUpdateResource,
890
984
  });
891
985
  artifacts = protocolV2.artifacts;
892
986
  targetsToUpdate = protocolV2.targets;
893
987
  }
894
988
  else {
895
989
  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)
990
+ const bootloaderRelease = asRelease(bootloader);
991
+ const integrity = requireArtifactIntegrity({
992
+ size: bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderExpectedSize,
993
+ sha256: bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderFingerprint,
994
+ }, 'Bootloader release');
995
+ 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
996
  ? {
901
- targetVersion: asVersion((_e = asRelease(bootloader)) === null || _e === void 0 ? void 0 : _e.bootloaderVersion),
997
+ targetVersion: asVersion(bootloaderRelease === null || bootloaderRelease === void 0 ? void 0 : bootloaderRelease.bootloaderVersion),
902
998
  }
903
999
  : {})));
904
1000
  }
@@ -909,10 +1005,8 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
909
1005
  });
910
1006
  }
911
1007
  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)
1008
+ const integrity = requireArtifactIntegrity({ size: firmwareRelease.expectedSize, sha256: firmwareRelease.fingerprint }, 'Firmware release');
1009
+ 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
1010
  ? { targetVersion: asVersion(firmwareRelease.version) }
917
1011
  : {})));
918
1012
  }
@@ -923,23 +1017,25 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
923
1017
  platform,
924
1018
  });
925
1019
  if (resourceUrl) {
926
- artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, asIntegrity({
1020
+ const integrity = requireArtifactIntegrity({
927
1021
  size: resourceUrl === asString(firmwareRelease.fullResource)
928
1022
  ? firmwareRelease.fullResourceExpectedSize
929
1023
  : firmwareRelease.resourceExpectedSize,
930
1024
  sha256: resourceUrl === asString(firmwareRelease.fullResource)
931
1025
  ? firmwareRelease.fullResourceFingerprint
932
1026
  : firmwareRelease.resourceFingerprint,
933
- })));
1027
+ }, 'Legacy resource archive');
1028
+ artifacts.push(Object.assign({ artifactId: 'resource', role: 'resource', target: 'resource', url: assertArtifactUrl(resourceUrl, 'Firmware resource release'), container: 'zip' }, integrity));
934
1029
  }
935
1030
  }
936
1031
  }
937
1032
  if (isUpgrade(ble) || isRecoveryInstall(ble) || forcedTargets.has('ble')) {
938
1033
  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({
1034
+ const integrity = requireArtifactIntegrity({
940
1035
  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)
1036
+ 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,
1037
+ }, 'BLE release');
1038
+ 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
1039
  ? { targetVersion: asVersion(bleRelease === null || bleRelease === void 0 ? void 0 : bleRelease.version) }
944
1040
  : {})));
945
1041
  }
@@ -951,25 +1047,13 @@ const buildFirmwareUpdatePlan = ({ features, firmwareType, platform, firmware, b
951
1047
  artifacts,
952
1048
  targetsToUpdate,
953
1049
  });
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)),
1050
+ return finalizeFirmwareUpdatePlan({
1051
+ features,
967
1052
  firmwareType,
968
1053
  platform,
969
1054
  artifacts,
970
1055
  targetsToUpdate,
971
- };
972
- return assertFirmwareUpdatePlan(Object.assign(Object.assign({}, planWithoutDigest), { planDigest: digestFirmwareUpdatePlan(planWithoutDigest) }));
1056
+ });
973
1057
  };
974
1058
 
975
1059
  const preparedPlanError = (message) => {
@@ -1090,7 +1174,7 @@ const prepareFirmwareUpdatePlan = ({ plan: value, leaseRef, artifacts, }) => {
1090
1174
  return Object.assign(Object.assign({}, planWithoutDigest), { preparedPlanDigest: digestPreparedPlan(planWithoutDigest) });
1091
1175
  };
1092
1176
  const validateFirmwareUpdatePreparedPlan = (value) => {
1093
- var _a, _b, _c;
1177
+ var _a, _b, _c, _d;
1094
1178
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
1095
1179
  return preparedPlanError('Firmware prepared plan must be an object');
1096
1180
  }
@@ -1170,7 +1254,13 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1170
1254
  artifactIds.add(artifact.artifactId);
1171
1255
  artifactTargets.add(artifact.target);
1172
1256
  assertFirmwareArtifactReference(artifact.artifact);
1173
- (_c = artifact.materializedEntries) === null || _c === void 0 ? void 0 : _c.forEach(assertPreparedEntry);
1257
+ const materializedEntryNames = (_d = (_c = artifact.materializedEntries) === null || _c === void 0 ? void 0 : _c.map(entry => {
1258
+ assertPreparedEntry(entry);
1259
+ return getFirmwareUpdateResourceName(entry.entryName).toLowerCase();
1260
+ })) !== null && _d !== void 0 ? _d : [];
1261
+ if (new Set(materializedEntryNames).size !== materializedEntryNames.length) {
1262
+ return preparedPlanError('Firmware prepared plan contains duplicate entry names');
1263
+ }
1174
1264
  }
1175
1265
  if (preparedPlan.targetsToUpdate.some(target => !FIRMWARE_UPDATE_PLAN_TARGETS.has(target)) ||
1176
1266
  new Set(preparedPlan.targetsToUpdate).size !== preparedPlan.targetsToUpdate.length ||
@@ -1184,6 +1274,14 @@ const validateFirmwareUpdatePreparedPlan = (value) => {
1184
1274
  }
1185
1275
  return preparedPlan;
1186
1276
  };
1277
+ const getFirmwareUpdatePreparedRawArtifact = ({ preparedPlan: value, target, role, }) => {
1278
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(value);
1279
+ const artifacts = preparedPlan.artifacts.filter(artifact => artifact.target === target);
1280
+ if (artifacts.length !== 1 || artifacts[0].role !== role || artifacts[0].container !== 'raw') {
1281
+ return preparedPlanError(`Firmware prepared plan ${target} artifact is invalid`);
1282
+ }
1283
+ return artifacts[0];
1284
+ };
1187
1285
  const degradedPlanIdentityPins = new Map();
1188
1286
  const DEGRADED_PLAN_IDENTITY_PIN_LIMIT = 32;
1189
1287
  const pinDegradedPlanIdentity = (leaseRef, deviceIdentity) => {
@@ -1204,7 +1302,7 @@ const assertFirmwareUpdatePreparedPlanDeviceIdentity = ({ preparedPlan: value, d
1204
1302
  }
1205
1303
  const pinnedIdentity = degradedPlanIdentityPins.get(preparedPlan.leaseRef);
1206
1304
  if (!deviceIdentity) {
1207
- if (bootloaderMode !== true) {
1305
+ if (preparedPlan.executor !== 'v4' && bootloaderMode !== true) {
1208
1306
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
1209
1307
  }
1210
1308
  return preparedPlan;
@@ -1277,6 +1375,8 @@ class FirmwareHostBindingRegistry {
1277
1375
  register(binding) {
1278
1376
  var _a;
1279
1377
  if (!binding ||
1378
+ typeof binding.preparedPlanDigest !== 'string' ||
1379
+ !/^[0-9a-f]{64}$/i.test(binding.preparedPlanDigest) ||
1280
1380
  typeof ((_a = binding.artifactReader) === null || _a === void 0 ? void 0 : _a.open) !== 'function' ||
1281
1381
  typeof binding.artifactReader.read !== 'function' ||
1282
1382
  typeof binding.artifactReader.close !== 'function') {
@@ -1297,7 +1397,7 @@ class FirmwareHostBindingRegistry {
1297
1397
  getGeneration() {
1298
1398
  return this.generation;
1299
1399
  }
1300
- resolve(generation) {
1400
+ resolve(generation, preparedPlanDigest) {
1301
1401
  if (!Number.isSafeInteger(generation) ||
1302
1402
  generation <= 0 ||
1303
1403
  generation !== this.generation ||
@@ -1305,12 +1405,17 @@ class FirmwareHostBindingRegistry {
1305
1405
  return bindingError(`Firmware host binding generation ${generation} is stale`);
1306
1406
  }
1307
1407
  const { binding } = this;
1408
+ if (preparedPlanDigest !== undefined &&
1409
+ binding.preparedPlanDigest.toLowerCase() !== preparedPlanDigest.toLowerCase()) {
1410
+ return bindingError(`Firmware host binding generation ${generation} does not match the prepared plan`);
1411
+ }
1308
1412
  const assertCurrent = () => {
1309
1413
  if (generation !== this.generation || binding !== this.binding) {
1310
1414
  bindingError(`Firmware host binding generation ${generation} is stale`);
1311
1415
  }
1312
1416
  };
1313
1417
  return {
1418
+ preparedPlanDigest: binding.preparedPlanDigest,
1314
1419
  artifactReader: {
1315
1420
  open: (input) => __awaiter(this, void 0, void 0, function* () {
1316
1421
  assertCurrent();
@@ -1343,7 +1448,7 @@ const firmwareHostBindingRegistry = new FirmwareHostBindingRegistry();
1343
1448
  const registerFirmwareUpdateHostBinding = (binding) => firmwareHostBindingRegistry.register(binding);
1344
1449
  const unregisterFirmwareUpdateHostBinding = (generation) => firmwareHostBindingRegistry.unregister(generation);
1345
1450
  const getFirmwareUpdateHostBindingGeneration = () => firmwareHostBindingRegistry.getGeneration();
1346
- const resolveFirmwareUpdateHostBinding = (generation) => firmwareHostBindingRegistry.resolve(generation !== null && generation !== void 0 ? generation : Number.NaN);
1451
+ const resolveFirmwareUpdateHostBinding = (generation, preparedPlanDigest) => firmwareHostBindingRegistry.resolve(generation !== null && generation !== void 0 ? generation : Number.NaN, preparedPlanDigest);
1347
1452
 
1348
1453
  const callbackManager = new Map();
1349
1454
  const generateCallbackId = () => `callback_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
@@ -40504,143 +40609,8 @@ const findLatestRelease = (releases) => {
40504
40609
  return leastRelease;
40505
40610
  };
40506
40611
 
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);
40612
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1 = 'vol0:/loaders/bootloader/boot_resource.okpkg';
40526
40613
  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
40614
  function normalizeHex$1(value, expectedLength, field) {
40645
40615
  if (typeof value !== 'string') {
40646
40616
  throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
@@ -40651,132 +40621,158 @@ function normalizeHex$1(value, expectedLength, field) {
40651
40621
  }
40652
40622
  return normalized;
40653
40623
  }
40654
- function validateResource(value, index) {
40624
+ function parseProtocolV2Resources(value) {
40625
+ if (value === undefined)
40626
+ return undefined;
40655
40627
  if (!value || typeof value !== 'object') {
40656
- throw new Error(`Invalid Pro2 resource at stable[${index}]`);
40628
+ throw new Error('Invalid Pro2 resources config');
40657
40629
  }
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}]`);
40630
+ const { source } = value;
40631
+ if (!source || typeof source !== 'object') {
40632
+ throw new Error('Invalid Pro2 resources config: source is required');
40661
40633
  }
40662
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
40663
- throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
40634
+ const { archiveUrl, archiveSha256, archiveSize } = source;
40635
+ if (typeof archiveUrl !== 'string' || !archiveUrl.startsWith('https://')) {
40636
+ throw new Error('Invalid Pro2 resources config: source.archiveUrl must use HTTPS');
40664
40637
  }
40665
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
40666
- throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
40638
+ if (typeof archiveSha256 !== 'string' || !/^[0-9a-fA-F]{64}$/.test(archiveSha256)) {
40639
+ throw new Error('Invalid Pro2 resources config: source.archiveSha256 must be a SHA-256 digest');
40640
+ }
40641
+ if (!Number.isSafeInteger(archiveSize) || archiveSize <= 0) {
40642
+ throw new Error('Invalid Pro2 resources config: source.archiveSize must be a positive integer');
40667
40643
  }
40668
40644
  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'),
40645
+ source: {
40646
+ archiveUrl,
40647
+ archiveSha256: archiveSha256.toLowerCase(),
40648
+ archiveSize: archiveSize,
40649
+ },
40674
40650
  };
40675
40651
  }
40676
- function validateBootResources(value) {
40677
- if (!value || typeof value !== 'object') {
40678
- throw new Error('Invalid Pro2 boot resources config');
40652
+ const PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS = [
40653
+ 'vol0:/bundles/',
40654
+ 'vol0:/loaders/rom/',
40655
+ ];
40656
+ function isAllowedManifestDevicePath(path) {
40657
+ if (!path.endsWith('.okpkg') ||
40658
+ path.includes('\\') ||
40659
+ path.includes('//') ||
40660
+ path.split('/').some(part => part === '.' || part === '..')) {
40661
+ return false;
40679
40662
  }
40680
- const resource = value;
40681
- if (resource.required !== false) {
40682
- throw new Error('Invalid Pro2 boot resources required flag: expected false');
40663
+ if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) {
40664
+ return true;
40683
40665
  }
40684
- if (resource.target !== 'RES') {
40685
- throw new Error('Invalid Pro2 boot resources target: expected RES');
40666
+ return PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS.some(root => path.startsWith(root));
40667
+ }
40668
+ function assertManifestString(value, field) {
40669
+ if (typeof value !== 'string' || value.length === 0) {
40670
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
40686
40671
  }
40687
- if (resource.manifestUrl !== undefined &&
40688
- (typeof resource.manifestUrl !== 'string' || !resource.manifestUrl.startsWith('https://'))) {
40689
- throw new Error('Invalid Pro2 boot resources manifestUrl');
40672
+ return value;
40673
+ }
40674
+ function assertManifestRelativePath(value, field) {
40675
+ const path = assertManifestString(value, field);
40676
+ if (path.startsWith('/') ||
40677
+ path.includes('\\') ||
40678
+ path.includes(':') ||
40679
+ path.split('/').some(part => !part || part === '.' || part === '..')) {
40680
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
40681
+ }
40682
+ return path;
40683
+ }
40684
+ function parseProtocolV2ResourceManifestFile(value, index) {
40685
+ var _a;
40686
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
40687
+ throw new Error(`Invalid Pro2 resource manifest files[${index}]`);
40690
40688
  }
40691
- if (!Array.isArray(resource.files) || resource.files.length === 0) {
40692
- throw new Error('Invalid Pro2 boot resources files');
40689
+ const file = value;
40690
+ const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
40691
+ const originalName = assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
40692
+ if (originalName.includes('/')) {
40693
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
40693
40694
  }
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');
40695
+ const devicePath = assertManifestString(file.device_path, `files[${index}].device_path`);
40696
+ if (!isAllowedManifestDevicePath(devicePath)) {
40697
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].device_path`);
40715
40698
  }
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');
40699
+ if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
40700
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
40725
40701
  }
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`);
40702
+ const digest = normalizeHex$1(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
40703
+ if (file.signed !== true) {
40704
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].signed`);
40731
40705
  }
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
- }
40706
+ if (file.sig_algo !== 'ed25519' && file.sig_algo !== 'mldsa65') {
40707
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].sig_algo`);
40736
40708
  }
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
- };
40709
+ if (file.payload_version !== null && typeof file.payload_version !== 'string') {
40710
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].payload_version`);
40752
40711
  }
40753
- if (!inventory) {
40754
- return mode === 'bootloader-recovery'
40755
- ? {
40756
- status: resources.length > 0 ? 'outdated' : 'valid',
40757
- resources: [...resources],
40758
- }
40759
- : { status: 'unknown', resources: [] };
40712
+ if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
40713
+ throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
40760
40714
  }
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
40715
  return {
40769
- status: changedResources.length === 0 ? 'valid' : 'outdated',
40770
- resources: changedResources,
40716
+ archive_path: archivePath,
40717
+ original_name: originalName,
40718
+ device_path: devicePath,
40719
+ size: Number(file.size),
40720
+ sha256: digest,
40721
+ signed: true,
40722
+ sig_algo: file.sig_algo,
40723
+ payload_version: (_a = file.payload_version) !== null && _a !== void 0 ? _a : null,
40771
40724
  };
40772
40725
  }
40773
- function bytesToHex$3(bytes) {
40774
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
40726
+ function parseProtocolV2ResourceManifest(value) {
40727
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
40728
+ throw new Error('Invalid Pro2 resource manifest');
40729
+ }
40730
+ const manifest = value;
40731
+ if (manifest.schema !== 1 ||
40732
+ manifest.variant !== 'resource' ||
40733
+ manifest.device_root !== 'vol0:' ||
40734
+ manifest.restore_mode !== 'bootloader_update' ||
40735
+ !Array.isArray(manifest.trees) ||
40736
+ !Array.isArray(manifest.files)) {
40737
+ throw new Error('Invalid Pro2 resource manifest contract');
40738
+ }
40739
+ const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
40740
+ const devicePaths = new Set(files.map(file => file.device_path));
40741
+ const archivePaths = new Set(files.map(file => file.archive_path));
40742
+ if (files.length === 0 ||
40743
+ devicePaths.size !== files.length ||
40744
+ archivePaths.size !== files.length ||
40745
+ !devicePaths.has(PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) ||
40746
+ !files.some(file => file.device_path.startsWith('vol0:/bundles/'))) {
40747
+ throw new Error('Invalid Pro2 resource manifest file set');
40748
+ }
40749
+ return {
40750
+ schema: 1,
40751
+ artifact_name: assertManifestString(manifest.artifact_name, 'artifact_name'),
40752
+ release_name: assertManifestString(manifest.release_name, 'release_name'),
40753
+ variant: 'resource',
40754
+ commit: assertManifestString(manifest.commit, 'commit'),
40755
+ short_sha: assertManifestString(manifest.short_sha, 'short_sha'),
40756
+ timestamp_utc: assertManifestString(manifest.timestamp_utc, 'timestamp_utc'),
40757
+ core_version: assertManifestString(manifest.core_version, 'core_version'),
40758
+ key_set: assertManifestString(manifest.key_set, 'key_set'),
40759
+ device_root: 'vol0:',
40760
+ restore_mode: 'bootloader_update',
40761
+ trees: manifest.trees.map((tree, index) => {
40762
+ if (!tree || typeof tree !== 'object') {
40763
+ throw new Error(`Invalid Pro2 resource manifest trees[${index}]`);
40764
+ }
40765
+ const item = tree;
40766
+ return {
40767
+ path: assertManifestRelativePath(item.path, `trees[${index}].path`),
40768
+ device: assertManifestString(item.device, `trees[${index}].device`),
40769
+ };
40770
+ }),
40771
+ files,
40772
+ };
40775
40773
  }
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();
40774
+ function selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate, }) {
40775
+ return targetsToUpdate.includes('resource') ? [...manifest.files] : [];
40780
40776
  }
40781
40777
 
40782
40778
  var _a$1;
@@ -40837,6 +40833,7 @@ class DataManager {
40837
40833
  return __awaiter(this, void 0, void 0, function* () {
40838
40834
  this.settings = settings;
40839
40835
  this.protocolV2ResourcesConfigError = undefined;
40836
+ this.protocolV2NeoResourcesConfigError = undefined;
40840
40837
  const manifestMode = (_b = settings.firmwareManifestMode) !== null && _b !== void 0 ? _b : (settings.fetchConfig ? 'sdk-managed' : undefined);
40841
40838
  if (settings.preloadedConfig) {
40842
40839
  this.applyRemoteConfig(settings.preloadedConfig);
@@ -40897,12 +40894,19 @@ class DataManager {
40897
40894
  let neoResources;
40898
40895
  try {
40899
40896
  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
40897
  }
40902
40898
  catch (error) {
40903
40899
  this.protocolV2ResourcesConfigError =
40904
40900
  error instanceof Error ? error : new Error(String(error));
40905
- Log$k.warn('[DataConfig] Ignoring invalid Protocol V2 resources config:', error);
40901
+ Log$k.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
40902
+ }
40903
+ try {
40904
+ neoResources = parseProtocolV2Resources((_c = data.neo) === null || _c === void 0 ? void 0 : _c.resources);
40905
+ }
40906
+ catch (error) {
40907
+ this.protocolV2NeoResourcesConfigError =
40908
+ error instanceof Error ? error : new Error(String(error));
40909
+ Log$k.warn('[DataConfig] Ignoring invalid Neo resources config:', error);
40906
40910
  }
40907
40911
  const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
40908
40912
  const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
@@ -40940,16 +40944,20 @@ class DataManager {
40940
40944
  }
40941
40945
  });
40942
40946
  }
40943
- static forceReloadData({ requireResources = false, } = {}) {
40947
+ static forceReloadData({ requireResources = false, resourceDeviceType = hdShared.EDeviceType.Pro2, } = {}) {
40944
40948
  return __awaiter(this, void 0, void 0, function* () {
40949
+ const resourceDeviceName = resourceDeviceType === hdShared.EDeviceType.Pro2 ? 'Pro2' : 'Neo';
40945
40950
  if (!this.settings) {
40946
40951
  throw new Error('Remote config settings are not initialized');
40947
40952
  }
40948
40953
  const hasFreshConfig = this.lastCheckTimestamp > 0 &&
40949
40954
  getTimeStamp() - this.lastCheckTimestamp <= FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS;
40950
40955
  if (hasFreshConfig) {
40951
- if (requireResources && this.protocolV2ResourcesConfigError) {
40952
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`);
40956
+ const resourcesConfigError = resourceDeviceType === hdShared.EDeviceType.Pro2
40957
+ ? this.protocolV2ResourcesConfigError
40958
+ : this.protocolV2NeoResourcesConfigError;
40959
+ if (requireResources && resourcesConfigError) {
40960
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`);
40953
40961
  }
40954
40962
  return;
40955
40963
  }
@@ -40957,19 +40965,18 @@ class DataManager {
40957
40965
  if (!loaded) {
40958
40966
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Unable to refresh the latest remote config');
40959
40967
  }
40960
- if (requireResources && this.protocolV2ResourcesConfigError) {
40961
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`);
40968
+ const resourcesConfigError = resourceDeviceType === hdShared.EDeviceType.Pro2
40969
+ ? this.protocolV2ResourcesConfigError
40970
+ : this.protocolV2NeoResourcesConfigError;
40971
+ if (requireResources && resourcesConfigError) {
40972
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`);
40962
40973
  }
40963
40974
  this.lastCheckTimestamp = getTimeStamp();
40964
40975
  });
40965
40976
  }
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) {
40977
+ static getProtocolV2ResourceSource(deviceType = hdShared.EDeviceType.Pro2) {
40971
40978
  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;
40979
+ return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.source;
40973
40980
  }
40974
40981
  static getProtobufMessages(schema = 'v1CurrentSchema') {
40975
40982
  return this.messages[schema];
@@ -41006,12 +41013,10 @@ DataManager.deviceMap = {
41006
41013
  ble: [],
41007
41014
  },
41008
41015
  [hdShared.EDeviceType.Pro2]: {
41009
- firmware: [],
41010
- ble: [],
41016
+ 'firmware-v1': [],
41011
41017
  },
41012
41018
  [hdShared.EDeviceType.Neo]: {
41013
- firmware: [],
41014
- ble: [],
41019
+ 'firmware-v1': [],
41015
41020
  },
41016
41021
  [hdShared.EDeviceType.ClassicPure]: {
41017
41022
  firmware: [],
@@ -42008,7 +42013,7 @@ const getFirmwareUpdateField = ({ features, updateType, targetVersion, firmwareT
42008
42013
  if (deviceType === hdShared.EDeviceType.Pro) {
42009
42014
  return latestFirmwareField;
42010
42015
  }
42011
- if (deviceType === hdShared.EDeviceType.Pro2) {
42016
+ if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo) {
42012
42017
  return 'firmware-v1';
42013
42018
  }
42014
42019
  return 'firmware';
@@ -42040,7 +42045,7 @@ const getFirmwareUpdateFieldArray = (features, updateType) => {
42040
42045
  if (deviceType === 'pro') {
42041
42046
  return ['firmware-v8'];
42042
42047
  }
42043
- if (deviceType === 'pro2') {
42048
+ if (deviceType === 'pro2' || deviceType === 'neo') {
42044
42049
  return ['firmware-v1'];
42045
42050
  }
42046
42051
  return ['firmware'];
@@ -47383,76 +47388,93 @@ class ClearSessionCache extends BaseMethod {
47383
47388
  }
47384
47389
  }
47385
47390
 
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);
47391
+ const PROTOCOL_V2_MAIN_FIRMWARE_TARGETS = [
47392
+ 'APPLICATION_P1',
47393
+ 'APPLICATION_P2',
47394
+ 'SE01',
47395
+ 'SE02',
47396
+ 'SE03',
47397
+ 'SE04',
47398
+ ];
47399
+ const PROTOCOL_V2_BLE_TARGETS = [
47400
+ 'COPROCESSOR',
47401
+ ];
47402
+ const PROTOCOL_V2_BOOTLOADER_TARGETS = [
47403
+ 'BOOTLOADER',
47404
+ ];
47405
+ function loadProtocolV2FirmwareReleaseContext({ device, firmwareType: firmwareTypeParam, checkFirmwareHash = false, methodName, }) {
47406
+ var _a;
47407
+ return __awaiter(this, void 0, void 0, function* () {
47408
+ const state = yield device.getDeviceState({
47409
+ refreshSections: checkFirmwareHash
47410
+ ? ['identity', 'versions', 'verification']
47411
+ : ['identity', 'versions'],
47412
+ });
47413
+ if (state.identity.deviceType !== 'pro2' && state.identity.deviceType !== 'neo') {
47414
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotSupportMethod, `${methodName} requires a Pro2 or Neo device for Protocol V2`);
47404
47415
  }
47405
- return Promise.resolve(null);
47406
- }
47416
+ const { features } = device;
47417
+ if (!features) {
47418
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `${methodName} requires initialized device features`);
47419
+ }
47420
+ const firmwareType = (_a = firmwareTypeParam !== null && firmwareTypeParam !== void 0 ? firmwareTypeParam : state.identity.firmwareType) !== null && _a !== void 0 ? _a : hdShared.EFirmwareType.Universal;
47421
+ return {
47422
+ state: state,
47423
+ features,
47424
+ firmwareType,
47425
+ release: DataManager.getFirmwareLatestRelease(features, firmwareType),
47426
+ };
47427
+ });
47407
47428
  }
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;
47429
+ function summarizeProtocolV2FirmwareRelease(plan, componentTargets) {
47430
+ var _a;
47431
+ const targetSet = new Set(componentTargets);
47432
+ const components = plan.components.filter(component => targetSet.has(component.componentTarget));
47433
+ const targetsToUpdate = Array.from(new Set(components.flatMap(component => component.status === 'outdated' && component.updateTarget ? [component.updateTarget] : [])));
47434
+ const hasUpgrade = targetsToUpdate.length > 0;
47435
+ const required = !!((_a = plan.release) === null || _a === void 0 ? void 0 : _a.required) && hasUpgrade;
47436
+ let status = 'valid';
47437
+ if (!plan.release || components.length === 0) {
47438
+ status = 'unavailable';
47418
47439
  }
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);
47440
+ else if (required) {
47441
+ status = 'required';
47442
+ }
47443
+ else if (hasUpgrade) {
47444
+ status = 'outdated';
47445
+ }
47446
+ else if (components.some(component => component.status === 'unknown')) {
47447
+ status = 'unknown';
47448
+ }
47449
+ else if (components.every(component => component.status === 'unsupported')) {
47450
+ status = 'unavailable';
47425
47451
  }
47452
+ return Object.assign(Object.assign({}, plan), { status,
47453
+ hasUpgrade,
47454
+ required,
47455
+ components,
47456
+ targetsToUpdate });
47426
47457
  }
47427
-
47428
- class CheckBridgeStatus extends BaseMethod {
47429
- init() {
47430
- this.useDevice = false;
47431
- this.useDevicePassphraseState = false;
47432
- this.skipForceUpdateCheck = true;
47458
+ function toProtocolV2FirmwareReleaseInfo({ plan, state, release, }) {
47459
+ return {
47460
+ shouldUpdate: plan.hasUpgrade,
47461
+ status: plan.status === 'unavailable' ? 'unknown' : plan.status,
47462
+ changelog: plan.release ? [plan.release.changelog] : [],
47463
+ release,
47464
+ bootloaderMode: state.status.mode === 'bootloader' || state.status.mode === 'romloader',
47465
+ };
47466
+ }
47467
+ function getProtocolV2ComponentReleaseInfo(plan, componentTarget) {
47468
+ var _a;
47469
+ const componentRelease = plan.components.find(component => component.componentTarget === componentTarget);
47470
+ if (!componentRelease || !plan.release) {
47471
+ return undefined;
47433
47472
  }
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
- });
47473
+ const component = (_a = plan.release.components) === null || _a === void 0 ? void 0 : _a[componentRelease.configKey];
47474
+ if (!component) {
47475
+ return undefined;
47455
47476
  }
47477
+ return Object.assign(Object.assign({}, component), { protocol: 'V2', configKey: componentRelease.configKey, componentTarget, required: plan.release.required, changelog: plan.release.changelog });
47456
47478
  }
47457
47479
 
47458
47480
  const REQUIRED_BRIDGE_VERSION = '2.2.0';
@@ -47509,56 +47531,6 @@ function getBridgeReleaseInfo({ deviceType, currentFirmwareVersion, willUpdateFi
47509
47531
  });
47510
47532
  }
47511
47533
 
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
47534
  const UPDATE_TARGET_BY_COMPONENT_TARGET = {
47563
47535
  BOOTLOADER: 'boot',
47564
47536
  APPLICATION_P1: 'app_v1',
@@ -47806,22 +47778,15 @@ class CheckAllFirmwareRelease extends BaseMethod {
47806
47778
  runProtocolV2() {
47807
47779
  var _a;
47808
47780
  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'],
47781
+ const { checkFirmwareHash = false, firmwareType: firmwareTypeParam, platform, forceUpdateTargets, protocolV2ForceUpdateTargets, } = this.payload;
47782
+ const validatedForceUpdateTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
47783
+ const validatedProtocolV2ForceUpdateTargets = validateProtocolV2FirmwareUpdateTargets(protocolV2ForceUpdateTargets);
47784
+ const { state, features, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47785
+ device: this.device,
47786
+ firmwareType: firmwareTypeParam,
47787
+ checkFirmwareHash,
47788
+ methodName: 'checkAllFirmwareRelease',
47815
47789
  });
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
47790
  const plan = buildProtocolV2FirmwareRelease({
47826
47791
  currentVersions: state.versions,
47827
47792
  currentVerification: checkFirmwareHash ? state.verification : undefined,
@@ -47831,48 +47796,250 @@ class CheckAllFirmwareRelease extends BaseMethod {
47831
47796
  deviceType: state.identity.deviceType,
47832
47797
  });
47833
47798
  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;
47799
+ const resourceSource = DataManager.getProtocolV2ResourceSource(resourceDeviceType);
47800
+ const resourceStatus = 'unknown';
47801
+ const detectedComponentTargets = validatedForceUpdateTargets.includes('firmware')
47802
+ ? plan.components.flatMap(component => component.updateTarget ? [component.updateTarget] : [])
47803
+ : plan.targetsToUpdate;
47804
+ const componentTargetsToUpdate = Array.from(new Set([
47805
+ ...detectedComponentTargets,
47806
+ ...validatedProtocolV2ForceUpdateTargets.filter(target => target !== 'resource'),
47807
+ ]));
47808
+ const forceResourceUpdate = validatedForceUpdateTargets.includes('resource') ||
47809
+ validatedProtocolV2ForceUpdateTargets.includes('resource');
47810
+ const targetsToUpdate = Array.from(new Set([
47811
+ ...componentTargetsToUpdate,
47812
+ ...(forceResourceUpdate ? ['resource'] : []),
47813
+ ]));
47814
+ let firmwareUpdatePlan;
47815
+ const shouldBuildFirmwareUpdatePlan = targetsToUpdate.length > 0;
47816
+ const requestedPlatform = platform !== null && platform !== void 0 ? platform : 'web';
47817
+ const requiresPreparedPlan = shouldBuildFirmwareUpdatePlan &&
47818
+ (requestedPlatform === 'native' || requestedPlatform === 'desktop');
47819
+ try {
47820
+ if (shouldBuildFirmwareUpdatePlan) {
47821
+ const validatedPlan = buildProtocolV2FirmwareUpdatePlan({
47822
+ features,
47823
+ firmwareType,
47824
+ platform: requestedPlatform,
47825
+ release,
47826
+ targetsToUpdate,
47827
+ forceUpdateTargets: validatedForceUpdateTargets,
47828
+ resourceArchive: resourceSource,
47829
+ });
47830
+ firmwareUpdatePlan = validatedPlan;
47831
+ }
47832
+ else {
47833
+ firmwareUpdatePlan = undefined;
47834
+ }
47835
+ }
47836
+ catch (error) {
47837
+ if (requiresPreparedPlan ||
47838
+ validatedForceUpdateTargets.length > 0 ||
47839
+ validatedProtocolV2ForceUpdateTargets.length > 0 ||
47840
+ !(error instanceof hdShared.HardwareError) ||
47841
+ ((_a = error.params) === null || _a === void 0 ? void 0 : _a.firmwareUpdateCode) !== 'FirmwarePlanInvalid') {
47842
+ throw error;
47843
+ }
47844
+ Log$c.warn('[CheckAllFirmwareRelease] Optional Protocol V2 firmware Plan is unavailable; using the release result');
47845
+ firmwareUpdatePlan = undefined;
47846
+ }
47847
+ const firmwarePlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_MAIN_FIRMWARE_TARGETS);
47848
+ const blePlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_BLE_TARGETS);
47849
+ const bootloaderPlan = summarizeProtocolV2FirmwareRelease(plan, PROTOCOL_V2_BOOTLOADER_TARGETS);
47850
+ const { status } = plan;
47851
+ return Object.assign(Object.assign({ firmware: toProtocolV2FirmwareReleaseInfo({ plan: firmwarePlan, state, release }), ble: toProtocolV2FirmwareReleaseInfo({
47852
+ plan: blePlan,
47853
+ state,
47854
+ release: getProtocolV2ComponentReleaseInfo(plan, 'COPROCESSOR'),
47855
+ }), bootloader: toProtocolV2FirmwareReleaseInfo({
47856
+ plan: bootloaderPlan,
47857
+ state,
47858
+ release: getProtocolV2ComponentReleaseInfo(plan, 'BOOTLOADER'),
47859
+ }), features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { status,
47860
+ resourceStatus, resourceArchive: resourceSource, hasUpgrade: targetsToUpdate.length > 0, targetsToUpdate,
47861
+ firmwareUpdatePlan });
47862
+ });
47863
+ }
47864
+ }
47865
+
47866
+ class CheckFirmwareRelease extends BaseMethod {
47867
+ getSupportedProtocols() {
47868
+ return ['V1', 'V2'];
47869
+ }
47870
+ init() {
47871
+ this.allowDeviceMode = [
47872
+ ...this.allowDeviceMode,
47873
+ UI_REQUEST.NOT_INITIALIZE,
47874
+ UI_REQUEST.BOOTLOADER,
47875
+ ];
47876
+ this.useDevicePassphraseState = false;
47877
+ this.skipForceUpdateCheck = true;
47878
+ }
47879
+ run() {
47880
+ var _a;
47881
+ return __awaiter(this, void 0, void 0, function* () {
47882
+ const payload = this.payload;
47883
+ if (!this.device.features)
47884
+ return null;
47885
+ if (this.device.isProtocolV2()) {
47886
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47887
+ device: this.device,
47888
+ firmwareType: payload.firmwareType,
47889
+ methodName: 'checkFirmwareRelease',
47890
+ });
47891
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
47892
+ currentVersions: state.versions,
47893
+ firmwareType,
47894
+ release,
47895
+ deviceType: state.identity.deviceType,
47896
+ }), PROTOCOL_V2_MAIN_FIRMWARE_TARGETS);
47897
+ return toProtocolV2FirmwareReleaseInfo({ plan, state, release });
47898
+ }
47899
+ const deviceFirmwareType = this.device.getCurrentFirmwareType();
47900
+ const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
47901
+ const releaseInfo = getFirmwareReleaseInfo(this.device.features, firmwareType);
47902
+ return releaseInfo;
47903
+ });
47904
+ }
47905
+ }
47906
+
47907
+ class CheckBLEFirmwareRelease extends BaseMethod {
47908
+ getSupportedProtocols() {
47909
+ return ['V1', 'V2'];
47910
+ }
47911
+ init() {
47912
+ this.allowDeviceMode = [
47913
+ ...this.allowDeviceMode,
47914
+ UI_REQUEST.NOT_INITIALIZE,
47915
+ UI_REQUEST.BOOTLOADER,
47916
+ ];
47917
+ this.useDevicePassphraseState = false;
47918
+ this.skipForceUpdateCheck = true;
47919
+ }
47920
+ run() {
47921
+ return __awaiter(this, void 0, void 0, function* () {
47922
+ if (!this.device.features)
47923
+ return null;
47924
+ if (this.device.isProtocolV2()) {
47925
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
47926
+ device: this.device,
47927
+ methodName: 'checkBLEFirmwareRelease',
47928
+ });
47929
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
47930
+ currentVersions: state.versions,
47931
+ firmwareType,
47932
+ release,
47933
+ deviceType: state.identity.deviceType,
47934
+ }), PROTOCOL_V2_BLE_TARGETS);
47935
+ return toProtocolV2FirmwareReleaseInfo({
47936
+ plan,
47937
+ state,
47938
+ release: getProtocolV2ComponentReleaseInfo(plan, 'COPROCESSOR'),
47939
+ });
47940
+ }
47941
+ const releaseInfo = getBleFirmwareReleaseInfo(this.device.features);
47942
+ return releaseInfo;
47943
+ });
47944
+ }
47945
+ }
47946
+
47947
+ class CheckBridgeStatus extends BaseMethod {
47948
+ init() {
47949
+ this.useDevice = false;
47950
+ this.useDevicePassphraseState = false;
47951
+ this.skipForceUpdateCheck = true;
47952
+ }
47953
+ run() {
47954
+ return __awaiter(this, void 0, void 0, function* () {
47955
+ return new Promise((resolve, reject) => {
47956
+ axios__default["default"]
47957
+ .request({
47958
+ url: 'http://localhost:21320',
47959
+ method: 'POST',
47960
+ withCredentials: false,
47961
+ timeout: 3000,
47962
+ })
47963
+ .then(() => resolve(true))
47964
+ .catch(e => {
47965
+ if (e.code === 'ECONNABORTED') {
47966
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BridgeTimeoutError));
47849
47967
  }
47850
- catch (_b) {
47851
- resourceStatus = buildProtocolV2ResourceUpdatePlan({
47852
- resources,
47853
- mode: 'bootloader-recovery',
47854
- }).status;
47968
+ else {
47969
+ resolve(false);
47855
47970
  }
47856
- }
47971
+ });
47972
+ });
47973
+ });
47974
+ }
47975
+ }
47976
+
47977
+ class CheckBridgeRelease extends BaseMethod {
47978
+ init() {
47979
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
47980
+ this.useDevicePassphraseState = false;
47981
+ this.skipForceUpdateCheck = true;
47982
+ }
47983
+ run() {
47984
+ return __awaiter(this, void 0, void 0, function* () {
47985
+ if (!this.device.features) {
47986
+ return null;
47857
47987
  }
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 });
47988
+ const { willUpdateFirmwareVersion } = this.payload;
47989
+ const { features } = this.device;
47990
+ const deviceType = getDeviceType(features);
47991
+ const currentFirmwareVersion = getDeviceFirmwareVersion(features).join('.');
47992
+ return getBridgeReleaseInfo({
47993
+ deviceType,
47994
+ currentFirmwareVersion,
47995
+ willUpdateFirmwareVersion,
47996
+ });
47997
+ });
47998
+ }
47999
+ }
48000
+
48001
+ class CheckBootloaderRelease extends BaseMethod {
48002
+ getSupportedProtocols() {
48003
+ return ['V1', 'V2'];
48004
+ }
48005
+ init() {
48006
+ this.allowDeviceMode = [...this.allowDeviceMode, UI_REQUEST.BOOTLOADER];
48007
+ this.useDevicePassphraseState = false;
48008
+ this.skipForceUpdateCheck = true;
48009
+ }
48010
+ run() {
48011
+ var _a;
48012
+ return __awaiter(this, void 0, void 0, function* () {
48013
+ if (!this.device.features)
48014
+ return null;
48015
+ const { features } = this.device;
48016
+ const payload = this.payload;
48017
+ if (this.device.isProtocolV2()) {
48018
+ const { state, firmwareType, release } = yield loadProtocolV2FirmwareReleaseContext({
48019
+ device: this.device,
48020
+ firmwareType: payload.firmwareType,
48021
+ methodName: 'checkBootloaderRelease',
48022
+ });
48023
+ const plan = summarizeProtocolV2FirmwareRelease(buildProtocolV2FirmwareRelease({
48024
+ currentVersions: state.versions,
48025
+ firmwareType,
48026
+ release,
48027
+ deviceType: state.identity.deviceType,
48028
+ }), PROTOCOL_V2_BOOTLOADER_TARGETS);
48029
+ return toProtocolV2FirmwareReleaseInfo({
48030
+ plan,
48031
+ state,
48032
+ release: getProtocolV2ComponentReleaseInfo(plan, 'BOOTLOADER'),
48033
+ });
48034
+ }
48035
+ const deviceFirmwareType = this.device.getCurrentFirmwareType();
48036
+ const firmwareType = (_a = payload.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
48037
+ const releaseInfo = getBootloaderReleaseInfo({
48038
+ features,
48039
+ willUpdateFirmwareVersion: payload.willUpdateFirmwareVersion,
48040
+ firmwareType,
48041
+ });
48042
+ return Promise.resolve(releaseInfo);
47876
48043
  });
47877
48044
  }
47878
48045
  }
@@ -49619,35 +49786,52 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49619
49786
  });
49620
49787
  }
49621
49788
  run() {
49622
- var _a, _b, _c;
49789
+ var _a, _b;
49623
49790
  return __awaiter(this, void 0, void 0, function* () {
49624
49791
  const { device } = this;
49625
49792
  const { features } = device;
49626
49793
  const payload = this.payload;
49627
- const hostBinding = payload.artifact
49628
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
49794
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
49795
+ if (hasPreparedPlan && payload.binary !== undefined) {
49796
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared bootloader plans cannot be combined with a legacy binary');
49797
+ }
49798
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
49799
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Bootloader artifacts require a prepared plan');
49800
+ }
49801
+ const preparedPlan = hasPreparedPlan
49802
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
49629
49803
  : 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) {
49804
+ const plannedArtifact = preparedPlan
49805
+ ? getFirmwareUpdatePreparedRawArtifact({
49806
+ preparedPlan,
49807
+ target: 'bootloader',
49808
+ role: 'bootloader',
49809
+ }).artifact
49810
+ : undefined;
49811
+ const artifactReader = preparedPlan
49812
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
49813
+ : payload.artifactReader;
49814
+ const executionParams = Object.assign(Object.assign({}, payload), { artifactReader });
49815
+ if (preparedPlan && plannedArtifact) {
49632
49816
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
49633
- preparedPlan: payload.preparedPlan,
49817
+ preparedPlan,
49634
49818
  deviceIdentity: getDeviceUUID(features) || undefined,
49635
49819
  bootloaderMode: resolveDeviceBootloaderMode(features),
49636
49820
  deviceModel: String(getDeviceType(features)),
49637
49821
  });
49638
49822
  assertFirmwareUpdatePreparedPlanBinding({
49639
- preparedPlan: payload.preparedPlan,
49823
+ preparedPlan,
49640
49824
  executor: 'v2',
49641
49825
  scopeTargets: ['bootloader'],
49642
49826
  bindings: [
49643
49827
  {
49644
49828
  target: 'bootloader',
49645
- artifact: payload.artifact,
49829
+ artifact: (_a = payload.artifact) !== null && _a !== void 0 ? _a : plannedArtifact,
49646
49830
  },
49647
49831
  ],
49648
49832
  });
49649
49833
  const source = yield openFirmwareByteSource({
49650
- artifact: payload.artifact,
49834
+ artifact: plannedArtifact,
49651
49835
  reader: executionParams.artifactReader,
49652
49836
  });
49653
49837
  if (!source) {
@@ -49655,13 +49839,11 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49655
49839
  }
49656
49840
  try {
49657
49841
  const deviceType = device.getCurrentDeviceType();
49658
- const deviceFirmwareType = device.getCurrentFirmwareType();
49659
- const firmwareType = (_b = payload.firmwareType) !== null && _b !== void 0 ? _b : deviceFirmwareType;
49660
49842
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
49661
49843
  return yield this.updateTouchBootloader({
49662
49844
  device,
49663
49845
  features,
49664
- firmwareType,
49846
+ firmwareType: preparedPlan.firmwareType,
49665
49847
  source,
49666
49848
  });
49667
49849
  }
@@ -49674,7 +49856,7 @@ class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod {
49674
49856
  const { binary } = payload;
49675
49857
  const deviceType = device.getCurrentDeviceType();
49676
49858
  const deviceFirmwareType = device.getCurrentFirmwareType();
49677
- const firmwareType = (_c = payload.firmwareType) !== null && _c !== void 0 ? _c : deviceFirmwareType;
49859
+ const firmwareType = (_b = payload.firmwareType) !== null && _b !== void 0 ? _b : deviceFirmwareType;
49678
49860
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
49679
49861
  return this.updateTouchBootloader({
49680
49862
  device,
@@ -49935,6 +50117,119 @@ const normalizeFirmwarePreparationError = (error) => {
49935
50117
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, error instanceof Error ? error.message : String(error));
49936
50118
  };
49937
50119
 
50120
+ const PREPARED_RESOURCE_ARCHIVE_MAX_BYTES = 256 * 1024 * 1024;
50121
+ const PREPARED_RESOURCE_ENTRY_MAX_COUNT = 512;
50122
+ const resourceArchiveError = (message, firmwareUpdateCode) => {
50123
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, message, { firmwareUpdateCode });
50124
+ };
50125
+ const getZipEntrySizes = (entry) => {
50126
+ var _a;
50127
+ const { compressedSize, uncompressedSize } = (_a = entry._data) !== null && _a !== void 0 ? _a : {};
50128
+ if (!Number.isSafeInteger(compressedSize) ||
50129
+ Number(compressedSize) < 0 ||
50130
+ !Number.isSafeInteger(uncompressedSize) ||
50131
+ Number(uncompressedSize) <= 0) {
50132
+ return resourceArchiveError(`Firmware prepared resource ZIP entry size is invalid: ${entry.name}`, 'FirmwareArtifactsNotPrepared');
50133
+ }
50134
+ return {
50135
+ compressedSize: Number(compressedSize),
50136
+ uncompressedSize: Number(uncompressedSize),
50137
+ };
50138
+ };
50139
+ const readVerifiedPreparedResourceArchive = ({ preparedPlan, reader, }) => __awaiter(void 0, void 0, void 0, function* () {
50140
+ const resourceArtifacts = preparedPlan.artifacts.filter(artifact => artifact.target === 'resource');
50141
+ if (!preparedPlan.targetsToUpdate.includes('resource')) {
50142
+ if (resourceArtifacts.length > 0) {
50143
+ return resourceArchiveError('Firmware prepared resource artifact is outside the approved targets', 'FirmwareArtifactsNotPrepared');
50144
+ }
50145
+ return [];
50146
+ }
50147
+ if (resourceArtifacts.length !== 1) {
50148
+ return resourceArchiveError('Firmware prepared plan must contain exactly one materialized resource ZIP', 'FirmwareArtifactsNotPrepared');
50149
+ }
50150
+ const archiveArtifact = resourceArtifacts[0];
50151
+ const preparedEntries = archiveArtifact.materializedEntries;
50152
+ if (archiveArtifact.role !== 'resource' ||
50153
+ archiveArtifact.container !== 'zip' ||
50154
+ !(preparedEntries === null || preparedEntries === void 0 ? void 0 : preparedEntries.length)) {
50155
+ return resourceArchiveError('Firmware prepared plan must contain exactly one materialized resource ZIP', 'FirmwareArtifactsNotPrepared');
50156
+ }
50157
+ const archiveSource = yield openFirmwareByteSource({
50158
+ artifact: archiveArtifact.artifact,
50159
+ reader,
50160
+ });
50161
+ if (!archiveSource) {
50162
+ return resourceArchiveError('Firmware prepared resource ZIP is unavailable', 'FirmwareArtifactsNotPrepared');
50163
+ }
50164
+ if (archiveSource.size > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES) {
50165
+ yield archiveSource.close().catch(() => undefined);
50166
+ return resourceArchiveError('Firmware prepared resource ZIP exceeds the archive size limit', 'FirmwareArtifactsNotPrepared');
50167
+ }
50168
+ let archiveBinary;
50169
+ try {
50170
+ archiveBinary = yield readFirmwareByteSourceFully(archiveSource);
50171
+ }
50172
+ finally {
50173
+ yield archiveSource.close().catch(() => undefined);
50174
+ }
50175
+ const archiveDigest = utils.bytesToHex(sha256.sha256(new Uint8Array(archiveBinary)));
50176
+ if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
50177
+ return resourceArchiveError('Firmware prepared resource ZIP does not match its approved receipt', 'FirmwareArtifactReceiptMismatch');
50178
+ }
50179
+ let zip;
50180
+ try {
50181
+ zip = yield JSZip__default["default"].loadAsync(archiveBinary);
50182
+ }
50183
+ catch (_a) {
50184
+ return resourceArchiveError('Firmware prepared resource ZIP cannot be parsed', 'FirmwareArtifactsNotPrepared');
50185
+ }
50186
+ const zipEntries = Object.values(zip.files);
50187
+ if (zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)) {
50188
+ return resourceArchiveError('Firmware prepared resource ZIP contains an unsafe entry path', 'FirmwareArtifactsNotPrepared');
50189
+ }
50190
+ const files = zipEntries.filter(entry => !entry.dir);
50191
+ if (files.length === 0 || files.length > PREPARED_RESOURCE_ENTRY_MAX_COUNT) {
50192
+ return resourceArchiveError('Firmware prepared resource ZIP entry set is invalid', 'FirmwareArtifactsNotPrepared');
50193
+ }
50194
+ let totalSize = 0;
50195
+ const canonicalNames = new Set();
50196
+ for (const entry of files) {
50197
+ const { compressedSize, uncompressedSize } = getZipEntrySizes(entry);
50198
+ const resourceName = getFirmwareUpdateResourceName(entry.name);
50199
+ const canonicalName = resourceName.toLowerCase();
50200
+ totalSize += uncompressedSize;
50201
+ if (compressedSize > archiveBinary.byteLength ||
50202
+ uncompressedSize > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES ||
50203
+ totalSize > PREPARED_RESOURCE_ARCHIVE_MAX_BYTES ||
50204
+ canonicalNames.has(canonicalName)) {
50205
+ return resourceArchiveError(`Firmware prepared resource ZIP entry bounds are invalid: ${entry.name}`, 'FirmwareArtifactsNotPrepared');
50206
+ }
50207
+ canonicalNames.add(canonicalName);
50208
+ }
50209
+ const preparedEntriesByName = new Map(preparedEntries.map(entry => [entry.entryName, entry]));
50210
+ if (preparedEntriesByName.size !== files.length || preparedEntries.length !== files.length) {
50211
+ return resourceArchiveError('Firmware prepared resource entries do not match the approved ZIP', 'FirmwareArtifactReceiptMismatch');
50212
+ }
50213
+ const verifiedEntries = [];
50214
+ for (const entry of files) {
50215
+ const { uncompressedSize } = getZipEntrySizes(entry);
50216
+ const binary = yield entry.async('arraybuffer');
50217
+ const preparedEntry = preparedEntriesByName.get(entry.name);
50218
+ const digest = utils.bytesToHex(sha256.sha256(new Uint8Array(binary)));
50219
+ if (binary.byteLength !== uncompressedSize ||
50220
+ !preparedEntry ||
50221
+ preparedEntry.artifact.size !== binary.byteLength ||
50222
+ preparedEntry.artifact.sha256.toLowerCase() !== digest) {
50223
+ return resourceArchiveError(`Firmware prepared resource entry does not match the approved ZIP: ${entry.name}`, 'FirmwareArtifactReceiptMismatch');
50224
+ }
50225
+ verifiedEntries.push({
50226
+ entryName: getFirmwareUpdateResourceName(entry.name),
50227
+ binary,
50228
+ });
50229
+ }
50230
+ return verifiedEntries;
50231
+ });
50232
+
49938
50233
  const Log$8 = getLogger(exports.LoggerNames.Method);
49939
50234
  const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
49940
50235
  connectTimeoutMs: 60000,
@@ -50009,7 +50304,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50009
50304
  };
50010
50305
  }
50011
50306
  init() {
50012
- var _a;
50307
+ var _a, _b;
50013
50308
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50014
50309
  this.requireDeviceMode = [];
50015
50310
  this.useDevicePassphraseState = false;
@@ -50022,11 +50317,12 @@ class FirmwareUpdateV2 extends BaseMethod {
50022
50317
  { name: 'platform', type: 'string', required: true },
50023
50318
  { name: 'firmwareType', type: 'string' },
50024
50319
  ]);
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');
50320
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
50321
+ if (hasPreparedPlan && (payload.binary !== undefined || payload.version !== undefined)) {
50322
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy firmware inputs');
50323
+ }
50324
+ if (!hasPreparedPlan && payload.artifact !== undefined) {
50325
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware artifacts require a prepared plan');
50030
50326
  }
50031
50327
  if (!payload.updateType) {
50032
50328
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'updateType is required');
@@ -50036,28 +50332,37 @@ class FirmwareUpdateV2 extends BaseMethod {
50036
50332
  forcedUpdateRes: payload.forcedUpdateRes,
50037
50333
  isUpdateBootloader: payload.isUpdateBootloader,
50038
50334
  };
50039
- if ('version' in payload) {
50335
+ if (!hasPreparedPlan && 'version' in payload) {
50040
50336
  this.params = Object.assign(Object.assign({}, this.params), { version: payload.version, firmwareType: payload.firmwareType });
50041
50337
  }
50042
- if ('binary' in payload) {
50338
+ if (!hasPreparedPlan && 'binary' in payload) {
50043
50339
  this.params = Object.assign(Object.assign({}, this.params), { binary: payload.binary });
50044
50340
  }
50045
- if ('artifact' in payload) {
50046
- const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration);
50341
+ if (hasPreparedPlan) {
50342
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(payload.preparedPlan);
50343
+ const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest);
50047
50344
  const target = payload.isUpdateBootloader ? 'bootloader' : payload.updateType;
50345
+ const plannedArtifact = getFirmwareUpdatePreparedRawArtifact({
50346
+ preparedPlan,
50347
+ target,
50348
+ role: target,
50349
+ }).artifact;
50048
50350
  const resourceBindings = ((_a = payload.resourceEntries) !== null && _a !== void 0 ? _a : []).map((entry) => ({
50049
50351
  target: 'resource',
50050
50352
  entryName: entry.entryName,
50051
50353
  artifact: entry.artifact,
50052
50354
  }));
50053
50355
  assertFirmwareUpdatePreparedPlanBinding({
50054
- preparedPlan: payload.preparedPlan,
50356
+ preparedPlan,
50055
50357
  executor: 'v2',
50056
50358
  platform: payload.platform,
50057
- scopeTargets: [target, ...(target === 'firmware' ? ['resource'] : [])],
50058
- bindings: [{ target, artifact: payload.artifact }, ...resourceBindings],
50359
+ scopeTargets: [
50360
+ target,
50361
+ ...(target === 'firmware' && resourceBindings.length ? ['resource'] : []),
50362
+ ],
50363
+ bindings: [{ target, artifact: (_b = payload.artifact) !== null && _b !== void 0 ? _b : plannedArtifact }, ...resourceBindings],
50059
50364
  });
50060
- this.params = Object.assign(Object.assign({}, this.params), { preparedPlan: payload.preparedPlan, artifact: payload.artifact, resourceEntries: payload.resourceEntries, artifactReader: hostBinding.artifactReader });
50365
+ this.params = Object.assign(Object.assign({}, this.params), { preparedPlan, artifact: plannedArtifact, artifactReader: hostBinding.artifactReader, firmwareType: preparedPlan.firmwareType });
50061
50366
  }
50062
50367
  }
50063
50368
  _promptDeviceInBootloaderForWebDevice() {
@@ -50207,7 +50512,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50207
50512
  }
50208
50513
  }
50209
50514
  run() {
50210
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
50515
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
50211
50516
  return __awaiter(this, void 0, void 0, function* () {
50212
50517
  const { device, params } = this;
50213
50518
  const { features, commands } = device;
@@ -50295,13 +50600,48 @@ class FirmwareUpdateV2 extends BaseMethod {
50295
50600
  throw normalizeFirmwarePreparationError(err);
50296
50601
  }
50297
50602
  });
50603
+ const preparedResourceRequested = !params.isUpdateBootloader &&
50604
+ params.updateType === 'firmware' &&
50605
+ ((_c = (_b = params.preparedPlan) === null || _b === void 0 ? void 0 : _b.targetsToUpdate.includes('resource')) !== null && _c !== void 0 ? _c : false);
50606
+ if (preparedResourceRequested && device.isBootloader()) {
50607
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware resources require the device application mode', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared', artifactName: 'resource' });
50608
+ }
50298
50609
  if (!device.isBootloader() && features) {
50299
50610
  const serialNo = device.getCurrentSerialNo();
50300
50611
  if (this.isEnteredManuallyBoot()) {
50301
50612
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateManuallyEnterBoot);
50302
50613
  }
50303
50614
  yield acquireFirmwareSource();
50304
- if (this.isSupportResourceUpdate(params.updateType)) {
50615
+ if (preparedResourceRequested) {
50616
+ if (!this.isSupportResourceUpdate(params.updateType)) {
50617
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Prepared firmware resources are not supported by this device', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared', artifactName: 'resource' });
50618
+ }
50619
+ this.postTipMessage('CheckLatestUiResource');
50620
+ this.postTipMessage('DownloadLatestUiResource');
50621
+ const verifiedEntries = yield readVerifiedPreparedResourceArchive({
50622
+ preparedPlan: params.preparedPlan,
50623
+ reader: params.artifactReader,
50624
+ });
50625
+ const sources = [];
50626
+ try {
50627
+ for (const entry of verifiedEntries) {
50628
+ const source = yield openFirmwareByteSource({ binary: entry.binary });
50629
+ if (!source) {
50630
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is empty`, {
50631
+ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
50632
+ artifactName: 'resource',
50633
+ });
50634
+ }
50635
+ sources.push({ entryName: entry.entryName, source });
50636
+ }
50637
+ yield updateResourcesFromSources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, sources);
50638
+ }
50639
+ finally {
50640
+ yield Promise.all(sources.map(entry => entry.source.close().catch(() => undefined)));
50641
+ }
50642
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
50643
+ }
50644
+ else if (this.isSupportResourceUpdate(params.updateType)) {
50305
50645
  this.postTipMessage('CheckLatestUiResource');
50306
50646
  const resourceUrl = DataManager.getSysResourcesLatestRelease({
50307
50647
  features,
@@ -50310,46 +50650,20 @@ class FirmwareUpdateV2 extends BaseMethod {
50310
50650
  });
50311
50651
  if (resourceUrl) {
50312
50652
  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);
50653
+ if (params.artifactReader ||
50654
+ DataManager.getSettings('firmwareManifestMode') === 'external-only') {
50655
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware resource must be prepared by the external firmware host', {
50656
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
50657
+ artifactName: 'resource',
50658
+ });
50349
50659
  }
50660
+ const resourceBinary = (yield getSysResourceBinary(resourceUrl))
50661
+ .binary;
50662
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
50663
+ yield updateResources(this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, resourceBinary);
50350
50664
  }
50351
50665
  }
50352
- (_d = (_c = this.device) === null || _c === void 0 ? void 0 : _c.commands) === null || _d === void 0 ? void 0 : _d.checkDisposed();
50666
+ (_e = (_d = this.device) === null || _d === void 0 ? void 0 : _d.commands) === null || _e === void 0 ? void 0 : _e.checkDisposed();
50353
50667
  try {
50354
50668
  this.postTipMessage('AutoRebootToBootloader');
50355
50669
  const bootRes = yield commands.typedCall('DeviceBackToBoot', 'Success');
@@ -50362,9 +50676,9 @@ class FirmwareUpdateV2 extends BaseMethod {
50362
50676
  DevicePool.clearDeviceCache(serialNo);
50363
50677
  }
50364
50678
  delete DevicePool.devicesCache[''];
50365
- yield ((_e = this.checkPromise) === null || _e === void 0 ? void 0 : _e.promise);
50679
+ yield ((_f = this.checkPromise) === null || _f === void 0 ? void 0 : _f.promise);
50366
50680
  this.checkPromise = null;
50367
- (_g = (_f = this.device) === null || _f === void 0 ? void 0 : _f.commands) === null || _g === void 0 ? void 0 : _g.checkDisposed();
50681
+ (_h = (_g = this.device) === null || _g === void 0 ? void 0 : _g.commands) === null || _h === void 0 ? void 0 : _h.checkDisposed();
50368
50682
  const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
50369
50683
  yield wait(isTouch ? 3000 : 1500);
50370
50684
  }
@@ -50377,7 +50691,7 @@ class FirmwareUpdateV2 extends BaseMethod {
50377
50691
  }
50378
50692
  }
50379
50693
  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();
50694
+ (_k = (_j = this.device) === null || _j === void 0 ? void 0 : _j.commands) === null || _k === void 0 ? void 0 : _k.checkDisposed();
50381
50695
  yield this.device.acquire();
50382
50696
  const response = yield uploadFirmwareFromSource(params.updateType, this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, source, true, params.isUpdateBootloader);
50383
50697
  if (this.connectId) {
@@ -50402,7 +50716,7 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50402
50716
  this.artifactSources = [];
50403
50717
  }
50404
50718
  init() {
50405
- var _a, _b, _c, _d, _e, _f;
50719
+ var _a, _b, _c;
50406
50720
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
50407
50721
  this.requireDeviceMode = [];
50408
50722
  this.useDevicePassphraseState = false;
@@ -50420,50 +50734,80 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50420
50734
  { name: 'firmwareType', type: 'string' },
50421
50735
  { name: 'platform', type: 'string' },
50422
50736
  ]);
50423
- const hostBinding = payload.artifacts || payload.preparedPlan
50424
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
50737
+ const hasPreparedPlan = payload.preparedPlan !== undefined;
50738
+ if (!hasPreparedPlan && payload.artifacts !== undefined) {
50739
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Firmware artifacts require a prepared plan');
50740
+ }
50741
+ const preparedPlan = hasPreparedPlan
50742
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
50425
50743
  : undefined;
50426
- if (hostBinding) {
50744
+ const hasLegacyInputs = [
50745
+ payload.bleVersion,
50746
+ payload.bleBinary,
50747
+ payload.firmwareVersion,
50748
+ payload.firmwareBinary,
50749
+ payload.resourceBinary,
50750
+ payload.bootloaderVersion,
50751
+ payload.bootloaderBinary,
50752
+ ].some(input => input !== undefined);
50753
+ if (preparedPlan && hasLegacyInputs) {
50754
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy firmware inputs');
50755
+ }
50756
+ const artifactReader = preparedPlan
50757
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader
50758
+ : payload.artifactReader;
50759
+ const preparedArtifacts = {};
50760
+ if (preparedPlan) {
50761
+ const componentTargets = ['bootloader', 'firmware', 'ble'];
50762
+ const supportedTargets = new Set([...componentTargets, 'resource']);
50763
+ const unsupportedTarget = preparedPlan.targetsToUpdate.find(target => !supportedTargets.has(target));
50764
+ if (unsupportedTarget) {
50765
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware prepared plan target ${unsupportedTarget} is not supported by V3`, { firmwareUpdateCode: 'FirmwarePreparedPlanInvalid' });
50766
+ }
50767
+ for (const target of componentTargets) {
50768
+ if (preparedPlan.targetsToUpdate.includes(target)) {
50769
+ preparedArtifacts[target] = getFirmwareUpdatePreparedRawArtifact({
50770
+ preparedPlan,
50771
+ target,
50772
+ role: target,
50773
+ }).artifact;
50774
+ }
50775
+ }
50776
+ const resourceBindings = ((_b = (_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a.resourceEntries) !== null && _b !== void 0 ? _b : []).map((entry) => ({
50777
+ target: 'resource',
50778
+ entryName: entry.entryName,
50779
+ artifact: entry.artifact,
50780
+ }));
50427
50781
  assertFirmwareUpdatePreparedPlanBinding({
50428
- preparedPlan: payload.preparedPlan,
50782
+ preparedPlan,
50429
50783
  executor: 'v3',
50430
50784
  platform: payload.platform,
50431
- scopeTargets: ['bootloader', 'firmware', 'resource', 'ble'],
50785
+ scopeTargets: [
50786
+ 'bootloader',
50787
+ 'firmware',
50788
+ 'ble',
50789
+ ...(resourceBindings.length ? ['resource'] : []),
50790
+ ],
50432
50791
  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
- })),
50792
+ ...componentTargets.flatMap(target => {
50793
+ var _a;
50794
+ const plannedArtifact = preparedArtifacts[target];
50795
+ const suppliedArtifact = (_a = payload.artifacts) === null || _a === void 0 ? void 0 : _a[target];
50796
+ return plannedArtifact || suppliedArtifact
50797
+ ? [
50798
+ {
50799
+ target,
50800
+ artifact: suppliedArtifact !== null && suppliedArtifact !== void 0 ? suppliedArtifact : plannedArtifact,
50801
+ },
50802
+ ]
50803
+ : [];
50804
+ }),
50805
+ ...resourceBindings,
50462
50806
  ],
50463
50807
  });
50464
50808
  }
50465
50809
  this.params = {
50466
- preparedPlan: payload.preparedPlan,
50810
+ preparedPlan,
50467
50811
  bleBinary: payload.bleBinary,
50468
50812
  firmwareBinary: payload.firmwareBinary,
50469
50813
  forcedUpdateRes: payload.forcedUpdateRes,
@@ -50472,10 +50816,10 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50472
50816
  bootloaderBinary: payload.bootloaderBinary,
50473
50817
  firmwareVersion: payload.firmwareVersion,
50474
50818
  resourceBinary: payload.resourceBinary,
50475
- firmwareType: payload.firmwareType,
50819
+ firmwareType: (_c = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _c !== void 0 ? _c : payload.firmwareType,
50476
50820
  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,
50821
+ artifactReader,
50822
+ artifacts: preparedPlan ? preparedArtifacts : undefined,
50479
50823
  };
50480
50824
  }
50481
50825
  run() {
@@ -50581,19 +50925,21 @@ class FirmwareUpdateV3 extends FirmwareUpdateBaseMethod {
50581
50925
  prepareResourceInput(firmwareType) {
50582
50926
  var _a;
50583
50927
  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) {
50928
+ if ((_a = this.params.preparedPlan) === null || _a === void 0 ? void 0 : _a.targetsToUpdate.includes('resource')) {
50929
+ const verifiedEntries = yield readVerifiedPreparedResourceArchive({
50930
+ preparedPlan: this.params.preparedPlan,
50931
+ reader: this.params.artifactReader,
50932
+ });
50589
50933
  const resourceEntries = [];
50590
- for (const entry of preparedEntries) {
50591
- const resourceName = getFirmwareUpdateResourceName(entry.entryName);
50592
- const source = yield this.openArtifactSource(undefined, entry.artifact);
50934
+ for (const entry of verifiedEntries) {
50935
+ const source = yield this.openArtifactSource(entry.binary, undefined);
50593
50936
  if (!source) {
50594
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is not prepared`);
50937
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Firmware resource entry ${entry.entryName} is empty`, {
50938
+ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
50939
+ artifactName: 'resource',
50940
+ });
50595
50941
  }
50596
- resourceEntries.push({ entryName: resourceName, source });
50942
+ resourceEntries.push({ entryName: entry.entryName, source });
50597
50943
  }
50598
50944
  return {
50599
50945
  resourceBinary: null,
@@ -51090,6 +51436,88 @@ const INSTALLABLE_FIRMWARE_TARGET_IDS = new Set([
51090
51436
  ]);
51091
51437
  new Map(Object.entries(ProtocolV2FirmwareTargetType).flatMap(([key, value]) => INSTALLABLE_FIRMWARE_TARGET_IDS.has(value) ? [[key, value]] : []));
51092
51438
 
51439
+ let memoryHostSequence = 0;
51440
+ const createReference = (binary, prefix) => {
51441
+ const digest = utils.bytesToHex(sha256.sha256(new Uint8Array(binary)));
51442
+ return {
51443
+ artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
51444
+ size: binary.byteLength,
51445
+ sha256: digest,
51446
+ };
51447
+ };
51448
+ function prepareFirmwareUpdateV4MemoryHost({ sdk, plan, artifacts, }) {
51449
+ if (plan.executor !== 'v4') {
51450
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory host only supports V4 plans');
51451
+ }
51452
+ memoryHostSequence += 1;
51453
+ const hostId = `${Date.now()}:${memoryHostSequence}`;
51454
+ const binaries = new Map();
51455
+ const inputs = artifacts.map((input, artifactIndex) => {
51456
+ var _a;
51457
+ const artifactBinary = new Uint8Array(input.binary).slice();
51458
+ const artifact = createReference(artifactBinary.buffer, `${hostId}:artifact:${artifactIndex}`);
51459
+ binaries.set(artifact.artifactRef, artifactBinary);
51460
+ const materializedEntries = (_a = input.materializedEntries) === null || _a === void 0 ? void 0 : _a.map((entry, entryIndex) => {
51461
+ const entryBinary = new Uint8Array(entry.binary).slice();
51462
+ const entryArtifact = createReference(entryBinary.buffer, `${hostId}:entry:${artifactIndex}:${entryIndex}`);
51463
+ binaries.set(entryArtifact.artifactRef, entryBinary);
51464
+ return {
51465
+ entryName: entry.entryName,
51466
+ artifact: entryArtifact,
51467
+ };
51468
+ });
51469
+ return Object.assign({ artifactId: input.artifactId, artifact }, ((materializedEntries === null || materializedEntries === void 0 ? void 0 : materializedEntries.length) ? { materializedEntries } : {}));
51470
+ });
51471
+ const preparedPlan = sdk.prepareFirmwareUpdatePlan({
51472
+ plan,
51473
+ leaseRef: `fwmemlease:${hostId}`,
51474
+ artifacts: inputs,
51475
+ });
51476
+ const readers = new Map();
51477
+ let readerSequence = 0;
51478
+ const artifactReader = {
51479
+ open({ artifactRef }) {
51480
+ const binary = binaries.get(artifactRef);
51481
+ if (!binary) {
51482
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory artifact is unavailable');
51483
+ }
51484
+ readerSequence += 1;
51485
+ const readerId = `fwmemreader:${hostId}:${readerSequence}`;
51486
+ readers.set(readerId, binary);
51487
+ return Promise.resolve({ readerId, size: binary.byteLength });
51488
+ },
51489
+ read({ readerId, offset, length }) {
51490
+ const binary = readers.get(readerId);
51491
+ if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
51492
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Firmware memory artifact read is invalid');
51493
+ }
51494
+ const data = binary.slice(offset, offset + length).buffer;
51495
+ return Promise.resolve({
51496
+ data,
51497
+ bytesRead: data.byteLength,
51498
+ eof: offset + length === binary.byteLength,
51499
+ });
51500
+ },
51501
+ close({ readerId }) {
51502
+ readers.delete(readerId);
51503
+ return Promise.resolve();
51504
+ },
51505
+ };
51506
+ const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({
51507
+ artifactReader,
51508
+ preparedPlanDigest: preparedPlan.preparedPlanDigest,
51509
+ });
51510
+ return {
51511
+ preparedPlan,
51512
+ hostBindingGeneration,
51513
+ release: () => {
51514
+ sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
51515
+ readers.clear();
51516
+ binaries.clear();
51517
+ },
51518
+ };
51519
+ }
51520
+
51093
51521
  const Log$6 = getLogger(exports.LoggerNames.Method);
51094
51522
  const SESSION_ERROR$1 = 'session not found';
51095
51523
  const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
@@ -51116,7 +51544,24 @@ const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
51116
51544
  const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
51117
51545
  const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
51118
51546
  const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
51547
+ const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
51548
+ const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
51549
+ const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
51119
51550
  const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set(['se03', 'se04']);
51551
+ const getProtocolV2ZipEntrySizes = (entry) => {
51552
+ var _a;
51553
+ const { compressedSize, uncompressedSize } = (_a = entry._data) !== null && _a !== void 0 ? _a : {};
51554
+ if (!Number.isSafeInteger(compressedSize) ||
51555
+ Number(compressedSize) < 0 ||
51556
+ !Number.isSafeInteger(uncompressedSize) ||
51557
+ Number(uncompressedSize) <= 0) {
51558
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource ZIP entry size is invalid: ${entry.name}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
51559
+ }
51560
+ return {
51561
+ compressedSize: Number(compressedSize),
51562
+ uncompressedSize: Number(uncompressedSize),
51563
+ };
51564
+ };
51120
51565
  function assertProtocolV2FirmwareTargetsSupported(deviceType, params) {
51121
51566
  var _a;
51122
51567
  const unsupportedTargets = new Set(((_a = params.targetsToUpdate) !== null && _a !== void 0 ? _a : []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target)));
@@ -51187,6 +51632,14 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
51187
51632
  },
51188
51633
  };
51189
51634
  const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`));
51635
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
51636
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
51637
+ const isProtocolV2BootResourcePackagePath = (devicePath) => typeof devicePath === 'string' &&
51638
+ devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase() ===
51639
+ PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH;
51640
+ const resolveProtocolV2ResourceWritePath = (devicePath) => isProtocolV2BootResourcePackagePath(devicePath)
51641
+ ? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
51642
+ : devicePath;
51190
51643
  const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51191
51644
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
51192
51645
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
@@ -51197,6 +51650,10 @@ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51197
51650
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
51198
51651
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
51199
51652
  ]);
51653
+ const PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET = new Map(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => [
51654
+ PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId),
51655
+ target,
51656
+ ]));
51200
51657
  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
51658
  const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map(Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value]));
51202
51659
  const PROTOCOL_V2_TARGET_STATUS_BY_DECODED_NAME = new Map([
@@ -51354,6 +51811,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51354
51811
  super(...arguments);
51355
51812
  this.protocolV2PreparedSources = [];
51356
51813
  this.protocolV2ExecutionInLoader = false;
51814
+ this.protocolV2BootResourceStagingSafe = false;
51357
51815
  this.protocolV2CompletedTargetVersions = new Map();
51358
51816
  this.protocolV2FinalStatusVerified = false;
51359
51817
  }
@@ -51361,7 +51819,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51361
51819
  return ['V2'];
51362
51820
  }
51363
51821
  init() {
51364
- var _a, _b, _c;
51822
+ var _a, _b;
51365
51823
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
51366
51824
  this.requireDeviceMode = [];
51367
51825
  this.unlockPolicy = 'unlock-before-run';
@@ -51392,54 +51850,85 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51392
51850
  { name: 'se02Binary', type: 'buffer' },
51393
51851
  { name: 'se03Binary', type: 'buffer' },
51394
51852
  { name: 'se04Binary', type: 'buffer' },
51853
+ { name: 'resourceArchiveBinary', type: 'buffer' },
51395
51854
  { name: 'firmwareType', type: 'string' },
51396
51855
  { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
51397
51856
  { name: 'platform', type: 'string' },
51398
51857
  { name: 'expectedDeviceId', type: 'string' },
51399
- { name: 'resourceFiles', type: 'array', allowEmpty: true },
51400
51858
  ]);
51401
51859
  if (payload.expectedDeviceId !== undefined &&
51402
51860
  (payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)) {
51403
51861
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Protocol V2 expected device identity is invalid');
51404
51862
  }
51405
- const hostBinding = payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
51406
- ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
51863
+ const preparedPlan = payload.preparedPlan
51864
+ ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
51407
51865
  : undefined;
51408
- if (hostBinding) {
51866
+ const hasLocalArtifacts = [
51867
+ payload.bootloaderBinary,
51868
+ payload.romloaderBinary,
51869
+ payload.applicationP1Binary,
51870
+ payload.applicationP2Binary,
51871
+ payload.coprocessorBinary,
51872
+ payload.se01Binary,
51873
+ payload.se02Binary,
51874
+ payload.se03Binary,
51875
+ payload.se04Binary,
51876
+ payload.resourceArchiveBinary,
51877
+ ].some(Boolean);
51878
+ if (preparedPlan && hasLocalArtifacts) {
51879
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter, 'Prepared firmware plans cannot be combined with legacy or local firmware inputs');
51880
+ }
51881
+ let { artifactReader } = payload;
51882
+ if (preparedPlan) {
51883
+ artifactReader = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration, preparedPlan.preparedPlanDigest).artifactReader;
51884
+ }
51885
+ else if (payload.hostBindingGeneration !== undefined) {
51886
+ artifactReader = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration).artifactReader;
51887
+ }
51888
+ if (preparedPlan) {
51409
51889
  assertFirmwareUpdatePreparedPlanBinding({
51410
- preparedPlan: payload.preparedPlan,
51890
+ preparedPlan,
51411
51891
  executor: 'v4',
51412
51892
  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
- ],
51893
+ scopeTargets: [],
51894
+ bindings: [],
51439
51895
  });
51896
+ if (payload.componentArtifacts) {
51897
+ const componentBindings = Object.entries(payload.componentArtifacts).flatMap(([target, artifact]) => artifact
51898
+ ? [
51899
+ {
51900
+ target: target,
51901
+ artifact: artifact,
51902
+ },
51903
+ ]
51904
+ : []);
51905
+ assertFirmwareUpdatePreparedPlanBinding({
51906
+ preparedPlan,
51907
+ executor: 'v4',
51908
+ platform: payload.platform,
51909
+ scopeTargets: componentBindings.map(binding => binding.target),
51910
+ bindings: componentBindings,
51911
+ });
51912
+ }
51440
51913
  }
51914
+ const preparedExpectedTargetVersions = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.artifacts.reduce((result, artifact) => {
51915
+ if (artifact.role === 'component' &&
51916
+ artifact.target !== 'resource' &&
51917
+ artifact.targetVersion &&
51918
+ PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.has(artifact.target)) {
51919
+ result[artifact.target] = artifact.targetVersion;
51920
+ }
51921
+ return result;
51922
+ }, {});
51923
+ const localTargetsToUpdate = (_a = payload.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.map((target) => target === 'boot_resources' ? 'resource' : target);
51924
+ if (payload.resourceArchiveBinary &&
51925
+ localTargetsToUpdate &&
51926
+ !localTargetsToUpdate.includes('resource')) {
51927
+ localTargetsToUpdate.push('resource');
51928
+ }
51929
+ const resolvedLocalTargetsToUpdate = localTargetsToUpdate !== null && localTargetsToUpdate !== void 0 ? localTargetsToUpdate : (payload.resourceArchiveBinary ? ['resource'] : undefined);
51441
51930
  this.params = {
51442
- preparedPlan: payload.preparedPlan,
51931
+ preparedPlan,
51443
51932
  chunkSize: payload.chunkSize,
51444
51933
  forcedUpdateRes: payload.forcedUpdateRes,
51445
51934
  bootloaderBinary: payload.bootloaderBinary,
@@ -51451,15 +51940,18 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51451
51940
  se02Binary: payload.se02Binary,
51452
51941
  se03Binary: payload.se03Binary,
51453
51942
  se04Binary: payload.se04Binary,
51454
- resourceFiles: payload.resourceFiles,
51455
- firmwareType: payload.firmwareType,
51456
- targetsToUpdate: payload.targetsToUpdate,
51457
- expectedTargetVersions: payload.expectedTargetVersions,
51943
+ resourceArchiveBinary: payload.resourceArchiveBinary,
51944
+ firmwareType: (_b = preparedPlan === null || preparedPlan === void 0 ? void 0 : preparedPlan.firmwareType) !== null && _b !== void 0 ? _b : payload.firmwareType,
51945
+ targetsToUpdate: preparedPlan
51946
+ ? [...preparedPlan.targetsToUpdate]
51947
+ : resolvedLocalTargetsToUpdate,
51948
+ expectedTargetVersions: preparedPlan
51949
+ ? preparedExpectedTargetVersions
51950
+ : payload.expectedTargetVersions,
51458
51951
  platform: payload.platform,
51459
51952
  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,
51953
+ artifactReader,
51954
+ componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
51463
51955
  };
51464
51956
  }
51465
51957
  getProtocolV2FirmwareChunkSize(direction, filePath) {
@@ -51490,7 +51982,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51490
51982
  });
51491
51983
  }
51492
51984
  runProtocolV2() {
51493
- var _a, _b, _c, _d, _e;
51985
+ var _a, _b, _c, _d, _e, _f;
51494
51986
  return __awaiter(this, void 0, void 0, function* () {
51495
51987
  yield this.captureProtocolV2PhysicalIdentity();
51496
51988
  const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
@@ -51502,85 +51994,80 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51502
51994
  const deviceFirmwareType = getFirmwareType(deviceFeatures);
51503
51995
  const firmwareType = (_a = this.params.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
51504
51996
  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)) {
51997
+ if (!this.params.preparedPlan &&
51998
+ this.params.resourceArchiveBinary &&
51999
+ ((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('resource'))) {
52000
+ const localMemoryHost = yield this.prepareProtocolV2LocalMemoryHost({
52001
+ features: deviceFeatures,
52002
+ firmwareType,
52003
+ });
52004
+ try {
52005
+ return yield this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
52006
+ }
52007
+ finally {
52008
+ localMemoryHost.release();
52009
+ }
52010
+ }
52011
+ const hasPreparedComponentArtifacts = Object.values((_c = this.params.componentArtifacts) !== null && _c !== void 0 ? _c : {}).some(Boolean);
52012
+ if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
51507
52013
  return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
51508
52014
  }
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);
52015
+ const wantsResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
51514
52016
  let fwBinaryMap = [];
51515
52017
  let bootloaderBinary = null;
51516
52018
  let installItems;
51517
- let resourceBundles;
51518
52019
  try {
51519
52020
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartDownloadFirmware);
51520
- resourceBundles = this.prepareExplicitProtocolV2ResourceFiles();
51521
52021
  fwBinaryMap = this.collectExplicitTargetBinaries();
51522
52022
  bootloaderBinary = this.prepareBootloaderBinary();
51523
- const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
51524
- if ((needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) &&
52023
+ const explicitInstallItems = this.buildProtocolV2InstallItems({
52024
+ bootloaderBinary,
52025
+ fwBinaryMap,
52026
+ });
52027
+ const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
52028
+ const needsRemoteFirmware = ((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.length)
52029
+ ? missingFirmwareTargets.length > 0
52030
+ : explicitInstallItems.length === 0;
52031
+ if (wantsResources) {
52032
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource archive must be provided through a local or external PreparedPlan', {
52033
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52034
+ });
52035
+ }
52036
+ if (needsRemoteFirmware &&
51525
52037
  (this.params.artifactReader ||
51526
52038
  DataManager.getSettings('firmwareManifestMode') === 'external-only')) {
51527
52039
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware artifacts must be prepared by the external firmware host', {
51528
52040
  firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
51529
52041
  });
51530
52042
  }
51531
- if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
51532
- yield DataManager.forceReloadData({
51533
- requireResources: needsRemoteResources || needsRemoteBootResources,
51534
- });
52043
+ if (needsRemoteFirmware) {
52044
+ yield DataManager.forceReloadData();
51535
52045
  }
51536
52046
  if (needsRemoteFirmware) {
51537
- const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures);
52047
+ const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures, explicitInstallItems);
51538
52048
  bootloaderBinary = remoteBinaries.bootloaderBinary;
51539
52049
  fwBinaryMap = remoteBinaries.fwBinaryMap;
51540
52050
  installItems = remoteBinaries.installItems;
51541
52051
  }
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);
51548
- }
52052
+ this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51549
52053
  }
51550
52054
  catch (err) {
52055
+ if (typeof err === 'object' &&
52056
+ err !== null &&
52057
+ 'params' in err &&
52058
+ ((_f = err.params) === null || _f === void 0 ? void 0 : _f.firmwareUpdateCode) === 'FirmwareArtifactsNotPrepared') {
52059
+ throw err;
52060
+ }
51551
52061
  if (err instanceof hdShared.HardwareError && err.errorCode === hdShared.HardwareErrorCode.NetworkError) {
51552
52062
  throw err;
51553
52063
  }
51554
52064
  throw normalizeFirmwarePreparationError(err);
51555
52065
  }
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) {
52066
+ if (!bootloaderBinary && fwBinaryMap.length === 0 && !(installItems === null || installItems === void 0 ? void 0 : installItems.length)) {
51561
52067
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
51562
52068
  }
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)));
52069
+ return this.executeProtocolV2Update(Object.assign({ fwBinaryMap,
52070
+ bootloaderBinary }, (installItems ? { installItems } : undefined)));
51584
52071
  });
51585
52072
  }
51586
52073
  openProtocolV2PreparedSource(artifact) {
@@ -51617,6 +52104,34 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51617
52104
  prepareProtocolV2InstallSources(firmwareType, features) {
51618
52105
  var _a, _b;
51619
52106
  return __awaiter(this, void 0, void 0, function* () {
52107
+ if (this.params.preparedPlan) {
52108
+ const requestedTargets = new Set(this.params.preparedPlan.targetsToUpdate.filter((target) => target !== 'resource'));
52109
+ const installSources = [];
52110
+ const preparedTargets = new Set();
52111
+ for (const artifact of this.params.preparedPlan.artifacts) {
52112
+ if (artifact.target !== 'resource') {
52113
+ const target = artifact.target;
52114
+ const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
52115
+ if (!requestedTargets.has(target) ||
52116
+ artifact.role !== 'component' ||
52117
+ artifact.container !== 'raw' ||
52118
+ !installTarget ||
52119
+ preparedTargets.has(target)) {
52120
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52121
+ }
52122
+ installSources.push(Object.assign(Object.assign({}, installTarget), { source: yield this.openProtocolV2PreparedSource(artifact.artifact) }));
52123
+ preparedTargets.add(target);
52124
+ }
52125
+ }
52126
+ const missingTarget = Array.from(requestedTargets).find(target => !preparedTargets.has(target));
52127
+ if (missingTarget) {
52128
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 ${missingTarget} artifact is not prepared`, {
52129
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52130
+ artifactName: missingTarget,
52131
+ });
52132
+ }
52133
+ return installSources;
52134
+ }
51620
52135
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
51621
52136
  if (!release) {
51622
52137
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware release is unavailable');
@@ -51647,39 +52162,278 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51647
52162
  return installSources;
51648
52163
  });
51649
52164
  }
51650
- prepareProtocolV2ResourceSources(firmwareType, features) {
51651
- var _a, _b, _c;
52165
+ prepareProtocolV2LocalMemoryHost({ features, firmwareType, }) {
52166
+ var _a, _b;
51652
52167
  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;
52168
+ const installItems = this.buildProtocolV2InstallItems({
52169
+ bootloaderBinary: this.prepareBootloaderBinary(),
52170
+ fwBinaryMap: this.collectExplicitTargetBinaries(),
52171
+ });
52172
+ const requestedComponentTargets = new Set(((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : []).filter((target) => target !== 'resource' && target !== 'boot_resources'));
52173
+ const localComponentTargets = new Set(installItems.flatMap(item => {
52174
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52175
+ return target ? [target] : [];
52176
+ }));
52177
+ const missingTarget = Array.from(requestedComponentTargets).find(target => !localComponentTargets.has(target));
52178
+ if (missingTarget) {
52179
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local update has no binary for requested target ${missingTarget}`, {
52180
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
52181
+ artifactName: missingTarget,
52182
+ });
52183
+ }
52184
+ const planArtifacts = [];
52185
+ const memoryArtifacts = [];
52186
+ for (const item of installItems) {
52187
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52188
+ if (!target || item.binary.byteLength <= 0) {
52189
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local firmware artifact is invalid: ${item.fileName}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52190
+ }
52191
+ const artifactId = `component:${target}`;
52192
+ planArtifacts.push(Object.assign({ artifactId,
52193
+ 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])
52194
+ ? { targetVersion: this.params.expectedTargetVersions[target] }
52195
+ : {})));
52196
+ memoryArtifacts.push({ artifactId, binary: item.binary });
52197
+ }
52198
+ const resourceArchive = yield this.prepareProtocolV2LocalResourceArchive(this.params.resourceArchiveBinary);
52199
+ const resourceArtifactId = 'resource:archive';
52200
+ planArtifacts.push({
52201
+ artifactId: resourceArtifactId,
52202
+ target: 'resource',
52203
+ container: 'zip',
52204
+ logicalName: 'protocol-v2-local-resource-archive',
52205
+ expectedSize: resourceArchive.binary.byteLength,
52206
+ expectedSha256: bytesToHex(sha256.sha256(new Uint8Array(resourceArchive.binary))),
52207
+ });
52208
+ memoryArtifacts.push({
52209
+ artifactId: resourceArtifactId,
52210
+ binary: resourceArchive.binary,
52211
+ materializedEntries: resourceArchive.materializedEntries,
52212
+ });
52213
+ const plan = buildProtocolV2LocalFirmwareUpdatePlan({
52214
+ features,
52215
+ firmwareType,
52216
+ platform: this.params.platform,
52217
+ artifacts: planArtifacts,
52218
+ });
52219
+ let memoryHost;
52220
+ try {
52221
+ memoryHost = prepareFirmwareUpdateV4MemoryHost({
52222
+ sdk: {
52223
+ prepareFirmwareUpdatePlan,
52224
+ registerFirmwareUpdateHostBinding,
52225
+ unregisterFirmwareUpdateHostBinding,
52226
+ },
52227
+ plan,
52228
+ artifacts: memoryArtifacts,
52229
+ });
52230
+ const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
52231
+ assertFirmwareUpdatePreparedPlanBinding({
52232
+ preparedPlan,
52233
+ executor: 'v4',
52234
+ platform: this.params.platform,
52235
+ scopeTargets: [],
52236
+ bindings: [],
52237
+ });
52238
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
52239
+ preparedPlan,
52240
+ deviceIdentity: this.protocolV2ExpectedSerialNumber,
52241
+ deviceModel: this.getProtocolV2PreparedPlanDeviceModel(features),
52242
+ });
52243
+ const hostBinding = resolveFirmwareUpdateHostBinding(memoryHost.hostBindingGeneration, preparedPlan.preparedPlanDigest);
52244
+ this.params.preparedPlan = preparedPlan;
52245
+ this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate];
52246
+ this.params.artifactReader = hostBinding.artifactReader;
52247
+ this.params.componentArtifacts = undefined;
52248
+ return memoryHost;
52249
+ }
52250
+ catch (error) {
52251
+ memoryHost === null || memoryHost === void 0 ? void 0 : memoryHost.release();
52252
+ throw error;
52253
+ }
52254
+ });
52255
+ }
52256
+ prepareProtocolV2LocalResourceArchive(binary) {
52257
+ var _a;
52258
+ return __awaiter(this, void 0, void 0, function* () {
52259
+ if (binary.byteLength <= 0 || binary.byteLength > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52260
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP archive size is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52261
+ }
52262
+ const zip = yield JSZip__default["default"].loadAsync(binary);
52263
+ const zipEntries = Object.values(zip.files);
52264
+ if (zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)) {
52265
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP contains an unsafe entry path', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52266
+ }
52267
+ const entries = zipEntries.filter(entry => !entry.dir);
52268
+ if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
52269
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP entry set is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52270
+ }
52271
+ let declaredUncompressedSize = 0;
52272
+ let declaredCompressedSize = 0;
52273
+ for (const entry of entries) {
52274
+ const sizes = getProtocolV2ZipEntrySizes(entry);
52275
+ declaredCompressedSize += sizes.compressedSize;
52276
+ declaredUncompressedSize += sizes.uncompressedSize;
52277
+ const entryLimit = entry.name === 'manifest.json'
52278
+ ? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
52279
+ : PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
52280
+ if (sizes.uncompressedSize > entryLimit ||
52281
+ declaredCompressedSize > binary.byteLength ||
52282
+ declaredUncompressedSize >
52283
+ PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES) {
52284
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP declared size exceeds the allowed limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52285
+ }
52286
+ }
52287
+ const manifestEntry = zip.file('manifest.json');
52288
+ if (!manifestEntry) {
52289
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP has no manifest.json', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52290
+ }
52291
+ const manifestBinary = yield manifestEntry.async('arraybuffer');
52292
+ if (manifestBinary.byteLength <= 0 ||
52293
+ manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES) {
52294
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource manifest size is invalid', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52295
+ }
52296
+ let manifestValue;
52297
+ try {
52298
+ manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
52299
+ }
52300
+ catch (error) {
52301
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource manifest is invalid: ${String(error)}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52302
+ }
52303
+ const manifest = parseProtocolV2ResourceManifest(manifestValue);
52304
+ const selectedFiles = selectProtocolV2ResourceManifestFiles({
52305
+ manifest,
52306
+ targetsToUpdate: (_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : [],
52307
+ });
52308
+ const expectedEntryNames = new Set([
52309
+ 'manifest.json',
52310
+ ...selectedFiles.map(file => file.archive_path),
52311
+ ]);
52312
+ if (entries.length !== expectedEntryNames.size ||
52313
+ entries.some(entry => !expectedEntryNames.has(entry.name))) {
52314
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 local resource ZIP contains an unexpected entry', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52315
+ }
52316
+ let totalSize = 0;
52317
+ const materializedEntries = [
52318
+ { entryName: 'manifest.json', binary: manifestBinary },
52319
+ ];
52320
+ for (const file of selectedFiles) {
52321
+ const entry = zip.file(file.archive_path);
52322
+ if (!entry) {
52323
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource ZIP is missing ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52324
+ }
52325
+ const { uncompressedSize } = getProtocolV2ZipEntrySizes(entry);
52326
+ totalSize += uncompressedSize;
52327
+ if (uncompressedSize !== file.size || totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52328
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource file declared size is invalid: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52329
+ }
52330
+ const fileBinary = yield entry.async('arraybuffer');
52331
+ const digest = bytesToHex(sha256.sha256(new Uint8Array(fileBinary)));
52332
+ if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
52333
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 local resource file does not match manifest: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52334
+ }
52335
+ materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
52336
+ }
52337
+ return { binary, materializedEntries };
52338
+ });
52339
+ }
52340
+ prepareProtocolV2ResourceSources() {
52341
+ var _a, _b;
52342
+ return __awaiter(this, void 0, void 0, function* () {
52343
+ const resourceRequested = (_b = (_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource')) !== null && _b !== void 0 ? _b : false;
51655
52344
  if (!resourceRequested) {
51656
52345
  return [];
51657
52346
  }
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]));
52347
+ const archiveSources = yield this.prepareProtocolV2ResourceArchiveSources();
52348
+ if (archiveSources) {
52349
+ return archiveSources;
52350
+ }
52351
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource archive is not prepared', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52352
+ });
52353
+ }
52354
+ prepareProtocolV2ResourceArchiveSources() {
52355
+ var _a, _b, _c, _d;
52356
+ return __awaiter(this, void 0, void 0, function* () {
52357
+ const archiveArtifacts = (_b = (_a = this.params.preparedPlan) === null || _a === void 0 ? void 0 : _a.artifacts.filter(artifact => artifact.role === 'resourceBundle' &&
52358
+ artifact.target === 'resource' &&
52359
+ artifact.container === 'zip')) !== null && _b !== void 0 ? _b : [];
52360
+ if (archiveArtifacts.length === 0) {
52361
+ return undefined;
52362
+ }
52363
+ if (archiveArtifacts.length !== 1) {
52364
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared plan must contain exactly one resource archive', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52365
+ }
52366
+ const archiveArtifact = archiveArtifacts[0];
52367
+ const archiveSource = yield this.openProtocolV2PreparedSource(archiveArtifact.artifact);
52368
+ if (archiveSource.size > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52369
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive exceeds the total size limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52370
+ }
52371
+ let archiveBinary;
52372
+ try {
52373
+ archiveBinary = yield readFirmwareByteSourceFully(archiveSource);
52374
+ }
52375
+ finally {
52376
+ yield archiveSource.close();
52377
+ }
52378
+ const archiveDigest = bytesToHex(sha256.sha256(new Uint8Array(archiveBinary)));
52379
+ if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
52380
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive does not match its approved receipt', { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52381
+ }
52382
+ const verifiedArchive = yield this.prepareProtocolV2LocalResourceArchive(archiveBinary);
52383
+ const entries = (_c = archiveArtifact.materializedEntries) !== null && _c !== void 0 ? _c : [];
52384
+ const entriesByName = new Map(entries.map(entry => [entry.entryName, entry]));
52385
+ if (entries.length !== verifiedArchive.materializedEntries.length ||
52386
+ verifiedArchive.materializedEntries.some(entry => {
52387
+ const preparedEntry = entriesByName.get(entry.entryName);
52388
+ const digest = bytesToHex(sha256.sha256(new Uint8Array(entry.binary)));
52389
+ return (!preparedEntry ||
52390
+ preparedEntry.artifact.size !== entry.binary.byteLength ||
52391
+ preparedEntry.artifact.sha256.toLowerCase() !== digest);
52392
+ })) {
52393
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource entries do not match the approved archive', { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52394
+ }
52395
+ const verifiedEntriesByName = new Map(verifiedArchive.materializedEntries.map(entry => [entry.entryName, entry.binary]));
52396
+ const manifestBinary = verifiedEntriesByName.get('manifest.json');
52397
+ if (!manifestBinary) {
52398
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive has no valid manifest.json', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52399
+ }
52400
+ let manifestValue;
52401
+ try {
52402
+ manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
52403
+ }
52404
+ catch (error) {
52405
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared resource manifest is invalid: ${String(error)}`, { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52406
+ }
52407
+ const manifest = parseProtocolV2ResourceManifest(manifestValue);
52408
+ const selectedFiles = selectProtocolV2ResourceManifestFiles({
52409
+ manifest,
52410
+ targetsToUpdate: (_d = this.params.targetsToUpdate) !== null && _d !== void 0 ? _d : [],
52411
+ });
52412
+ if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
52413
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive contains too many files', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52414
+ }
52415
+ let totalSize = 0;
51661
52416
  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
- });
52417
+ for (const [index, file] of selectedFiles.entries()) {
52418
+ const binary = verifiedEntriesByName.get(file.archive_path);
52419
+ if (!binary || binary.byteLength !== file.size) {
52420
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
51669
52421
  }
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}`);
52422
+ totalSize += binary.byteLength;
52423
+ if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
52424
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 prepared resource archive exceeds the total size limit', { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' });
52425
+ }
52426
+ const source = yield this.openProtocolV2MemorySource(binary);
52427
+ const header = source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
52428
+ ? parseProtocolV2OkppHeader(new Uint8Array(yield source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE)))
52429
+ : null;
52430
+ sources.push(Object.assign({ name: file.original_name || `resource-${index}`, source, devicePath: file.device_path }, (header
52431
+ ? {
52432
+ version: header.version,
52433
+ payloadHash: header.payloadHash,
52434
+ headerHash: header.headerHash,
52435
+ }
52436
+ : {})));
51683
52437
  }
51684
52438
  return sources;
51685
52439
  });
@@ -51689,7 +52443,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51689
52443
  try {
51690
52444
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartDownloadFirmware);
51691
52445
  const installSources = yield this.prepareProtocolV2InstallSources(firmwareType, features);
51692
- const resourceSources = yield this.prepareProtocolV2ResourceSources(firmwareType, features);
52446
+ const resourceSources = yield this.prepareProtocolV2ResourceSources();
51693
52447
  if (installSources.length === 0 && resourceSources.length === 0) {
51694
52448
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
51695
52449
  }
@@ -51773,15 +52527,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51773
52527
  }
51774
52528
  }
51775
52529
  getProtocolV2RequestedTargets() {
51776
- var _a, _b, _c, _d;
52530
+ var _a, _b, _c;
51777
52531
  if ((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.length) {
51778
52532
  return [...new Set(this.params.targetsToUpdate)];
51779
52533
  }
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');
52534
+ if ((_b = this.params.preparedPlan) === null || _b === void 0 ? void 0 : _b.targetsToUpdate.length) {
52535
+ return [...new Set(this.params.preparedPlan.targetsToUpdate)];
51784
52536
  }
52537
+ const targets = new Set();
52538
+ Object.keys((_c = this.params.componentArtifacts) !== null && _c !== void 0 ? _c : {}).forEach(target => targets.add(target));
51785
52539
  if (this.params.bootloaderBinary)
51786
52540
  targets.add('boot');
51787
52541
  if (this.params.applicationP1Binary)
@@ -51818,8 +52572,26 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51818
52572
  const serialNumber = (_b = (_a = deviceInfo.hw) === null || _a === void 0 ? void 0 : _a.serial_no) === null || _b === void 0 ? void 0 : _b.trim();
51819
52573
  return serialNumber || undefined;
51820
52574
  }
52575
+ getProtocolV2PreparedPlanDeviceModel(features) {
52576
+ const currentDeviceType = this.device.getCurrentDeviceType();
52577
+ const featureDeviceType = features ? getDeviceType(features) : undefined;
52578
+ const deviceType = currentDeviceType === hdShared.EDeviceType.Pro2 || currentDeviceType === hdShared.EDeviceType.Neo
52579
+ ? currentDeviceType
52580
+ : featureDeviceType;
52581
+ return deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo
52582
+ ? String(deviceType)
52583
+ : undefined;
52584
+ }
52585
+ getProtocolV2ConnectionRoute() {
52586
+ var _a, _b, _c, _d, _e;
52587
+ const descriptor = this.device.originalDescriptor;
52588
+ return (((_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.path) === null || _a === void 0 ? void 0 : _a.trim()) ||
52589
+ ((_d = (_c = (_b = this.device).getConnectId) === null || _c === void 0 ? void 0 : _c.call(_b)) === null || _d === void 0 ? void 0 : _d.trim()) ||
52590
+ ((_e = descriptor === null || descriptor === void 0 ? void 0 : descriptor.id) === null || _e === void 0 ? void 0 : _e.trim()) ||
52591
+ undefined);
52592
+ }
51821
52593
  assertProtocolV2DeviceInfoIdentity(deviceInfo) {
51822
- assertProtocolV2ReconnectIdentity(this.protocolV2ExpectedSerialNumber, this.getProtocolV2SerialNumber(deviceInfo), this.protocolV2ExpectedPath, this.device.originalDescriptor.path);
52594
+ assertProtocolV2ReconnectIdentity(this.protocolV2ExpectedSerialNumber, this.getProtocolV2SerialNumber(deviceInfo), this.protocolV2ExpectedPath, this.getProtocolV2ConnectionRoute());
51823
52595
  }
51824
52596
  getProtocolV2DeviceInfoTimeout() {
51825
52597
  var _a;
@@ -51836,19 +52608,20 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51836
52608
  });
51837
52609
  }
51838
52610
  captureProtocolV2PhysicalIdentity() {
51839
- var _a, _b, _c, _d, _e;
52611
+ var _a, _b, _c;
51840
52612
  return __awaiter(this, void 0, void 0, function* () {
51841
52613
  const deviceInfo = yield this.requestProtocolV2PhysicalIdentity();
51842
52614
  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) {
52615
+ const path = this.getProtocolV2ConnectionRoute();
52616
+ if ((_a = this.params) === null || _a === void 0 ? void 0 : _a.preparedPlan) {
51845
52617
  assertFirmwareUpdatePreparedPlanDeviceIdentity({
51846
52618
  preparedPlan: this.params.preparedPlan,
51847
52619
  deviceIdentity: serialNumber,
52620
+ deviceModel: this.getProtocolV2PreparedPlanDeviceModel(this.device.features),
51848
52621
  });
51849
52622
  }
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);
52623
+ else if ((_b = this.params) === null || _b === void 0 ? void 0 : _b.expectedDeviceId) {
52624
+ assertProtocolV2ReconnectIdentity((_c = this.params) === null || _c === void 0 ? void 0 : _c.expectedDeviceId, serialNumber);
51852
52625
  }
51853
52626
  else {
51854
52627
  assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
@@ -51868,11 +52641,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51868
52641
  var _a;
51869
52642
  return (_a = this.params.bootloaderBinary) !== null && _a !== void 0 ? _a : null;
51870
52643
  }
51871
- hasExplicitProtocolV2Payload(fwBinaryMap) {
52644
+ getMissingProtocolV2FirmwareTargets(installItems) {
51872
52645
  var _a;
51873
- return (!!((_a = this.params.resourceFiles) === null || _a === void 0 ? void 0 : _a.length) ||
51874
- !!this.params.bootloaderBinary ||
51875
- fwBinaryMap.length > 0);
52646
+ const preparedTargets = new Set(installItems.flatMap(item => {
52647
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
52648
+ return target ? [target] : [];
52649
+ }));
52650
+ return ((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : [])
52651
+ .filter((target) => target !== 'resource' && target !== 'boot_resources')
52652
+ .filter(target => !preparedTargets.has(target));
51876
52653
  }
51877
52654
  buildProtocolV2InstallItems({ bootloaderBinary, fwBinaryMap, }) {
51878
52655
  const installItems = [];
@@ -51921,7 +52698,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51921
52698
  if (!component.url) {
51922
52699
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Missing Protocol V2 firmware component url: ${key}/${component.target}`);
51923
52700
  }
52701
+ const expectedFingerprint = normalizeProtocolV2Hex(component.fingerprint);
52702
+ if (!Number.isSafeInteger(component.expectedSize) ||
52703
+ Number(component.expectedSize) <= 0 ||
52704
+ !expectedFingerprint ||
52705
+ !/^[0-9a-f]{64}$/u.test(expectedFingerprint)) {
52706
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware component integrity metadata is invalid: ${key}/${component.target}`, { firmwareUpdateCode: 'FirmwarePlanInvalid' });
52707
+ }
51924
52708
  const { binary } = yield getSysResourceBinary(component.url);
52709
+ if (binary.byteLength !== component.expectedSize) {
52710
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware size mismatch: ${key}/${component.target}`, { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' });
52711
+ }
51925
52712
  if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
51926
52713
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`);
51927
52714
  }
@@ -51935,7 +52722,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51935
52722
  return Object.assign(Object.assign({}, target), { binary });
51936
52723
  });
51937
52724
  }
51938
- prepareRemoteProtocolV2Binaries(firmwareType, features) {
52725
+ prepareRemoteProtocolV2Binaries(firmwareType, features, explicitInstallItems = []) {
51939
52726
  var _a, _b;
51940
52727
  return __awaiter(this, void 0, void 0, function* () {
51941
52728
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
@@ -51943,6 +52730,10 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51943
52730
  const fwBinaryMap = [];
51944
52731
  const installItems = [];
51945
52732
  if (!release) {
52733
+ const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
52734
+ if (missingFirmwareTargets.length > 0) {
52735
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 firmware release is unavailable for requested targets: ${missingFirmwareTargets.join(', ')}`);
52736
+ }
51946
52737
  return {
51947
52738
  bootloaderBinary,
51948
52739
  fwBinaryMap,
@@ -51951,6 +52742,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51951
52742
  }
51952
52743
  const entries = this.getRemoteComponentEntries(release);
51953
52744
  const targetsToUpdate = new Set((_a = this.params.targetsToUpdate) !== null && _a !== void 0 ? _a : []);
52745
+ const explicitInstallItemByTargetId = new Map(explicitInstallItems.map(item => [item.targetId, item]));
52746
+ const preparedTargets = new Set();
51954
52747
  for (const [key, component] of entries) {
51955
52748
  const targetName = (_b = component.target) === null || _b === void 0 ? void 0 : _b.toUpperCase();
51956
52749
  const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
@@ -51958,27 +52751,27 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51958
52751
  ? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
51959
52752
  : undefined;
51960
52753
  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
- });
52754
+ const explicitInstallItem = explicitInstallItemByTargetId.get(target.targetId);
52755
+ const installItem = explicitInstallItem !== null && explicitInstallItem !== void 0 ? explicitInstallItem : (yield this.downloadRemoteProtocolV2Component(key, component));
52756
+ if (installItem.kind === 'bootloader') {
52757
+ bootloaderBinary = installItem.binary;
51970
52758
  }
51971
52759
  else {
51972
52760
  const binaryEntry = {
51973
- fileName: remoteBinary.fileName,
51974
- binary: remoteBinary.binary,
51975
- targetId: remoteBinary.targetId,
52761
+ fileName: installItem.fileName,
52762
+ binary: installItem.binary,
52763
+ targetId: installItem.targetId,
51976
52764
  };
51977
52765
  fwBinaryMap.push(binaryEntry);
51978
- installItems.push(Object.assign(Object.assign({}, binaryEntry), { kind: remoteBinary.kind }));
51979
52766
  }
52767
+ installItems.push(Object.assign({}, installItem));
52768
+ preparedTargets.add(updateTarget);
51980
52769
  }
51981
52770
  }
52771
+ const missingTarget = Array.from(targetsToUpdate).find(target => target !== 'resource' && !preparedTargets.has(target));
52772
+ if (missingTarget) {
52773
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol V2 release does not contain requested target ${missingTarget}`);
52774
+ }
51982
52775
  return {
51983
52776
  bootloaderBinary,
51984
52777
  fwBinaryMap,
@@ -51986,178 +52779,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51986
52779
  };
51987
52780
  });
51988
52781
  }
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
52782
  getProtocolV2ResourceFilePath(path) {
52162
52783
  if (path.startsWith('vol'))
52163
52784
  return path;
@@ -52165,7 +52786,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52165
52786
  return `vol0:${path}`;
52166
52787
  return `vol0:/${path}`;
52167
52788
  }
52168
- readProtocolV2DeviceFileHeader(path) {
52789
+ readProtocolV2DeviceFileHeader(path, expectedSize) {
52169
52790
  var _a, _b, _c, _d;
52170
52791
  return __awaiter(this, void 0, void 0, function* () {
52171
52792
  const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
@@ -52177,7 +52798,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52177
52798
  if (!((_b = pathInfoRes.message) === null || _b === void 0 ? void 0 : _b.exist) ||
52178
52799
  ((_c = pathInfoRes.message) === null || _c === void 0 ? void 0 : _c.directory) ||
52179
52800
  fileSize === undefined ||
52180
- fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE) {
52801
+ fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE ||
52802
+ (expectedSize !== undefined && fileSize !== expectedSize)) {
52181
52803
  return null;
52182
52804
  }
52183
52805
  const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
@@ -52214,10 +52836,10 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52214
52836
  return __awaiter(this, void 0, void 0, function* () {
52215
52837
  if ((_a = this.params) === null || _a === void 0 ? void 0 : _a.forcedUpdateRes)
52216
52838
  return false;
52217
- if (!bundle.version && !bundle.payloadHash)
52839
+ if (!bundle.payloadHash || !bundle.headerHash)
52218
52840
  return false;
52219
52841
  try {
52220
- const header = yield this.readProtocolV2DeviceFileHeader(bundle.devicePath);
52842
+ const header = yield this.readProtocolV2DeviceFileHeader(bundle.devicePath, bundle.source.size);
52221
52843
  if (!header)
52222
52844
  return false;
52223
52845
  if (bundle.version) {
@@ -52225,16 +52847,12 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52225
52847
  if (cmp === undefined || cmp !== 0)
52226
52848
  return false;
52227
52849
  }
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
- }
52850
+ const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
52851
+ const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
52852
+ if (!expectedPayloadHash || header.payloadHash !== expectedPayloadHash)
52853
+ return false;
52854
+ if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash)
52855
+ return false;
52238
52856
  return true;
52239
52857
  }
52240
52858
  catch (error) {
@@ -52349,11 +52967,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52349
52967
  }
52350
52968
  buildProtocolV2ExecutionPhases({ installSources, resourceSources, }) {
52351
52969
  const phases = [];
52352
- if (resourceSources.length > 0) {
52970
+ const bootResourceSources = resourceSources.filter(source => isProtocolV2BootResourcePackagePath(source.devicePath));
52971
+ const remainingResourceSources = resourceSources.filter(source => !isProtocolV2BootResourcePackagePath(source.devicePath));
52972
+ if (bootResourceSources.length > 0) {
52353
52973
  phases.push({
52354
52974
  kind: 'resource-sync',
52355
52975
  installSources: [],
52356
- resourceSources,
52976
+ resourceSources: bootResourceSources,
52357
52977
  });
52358
52978
  }
52359
52979
  const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
@@ -52368,6 +52988,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52368
52988
  resourceSources: [],
52369
52989
  });
52370
52990
  }
52991
+ if (remainingResourceSources.length > 0) {
52992
+ phases.push({
52993
+ kind: 'resource-sync',
52994
+ installSources: [],
52995
+ resourceSources: remainingResourceSources,
52996
+ });
52997
+ }
52371
52998
  const componentSources = installSources.filter(source => source.kind !== 'bootloader');
52372
52999
  if (componentSources.length > 0) {
52373
53000
  phases.push({
@@ -52385,6 +53012,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52385
53012
  }
52386
53013
  executeProtocolV2Phases({ installSources, resourceSources, }) {
52387
53014
  return __awaiter(this, void 0, void 0, function* () {
53015
+ this.protocolV2BootResourceStagingSafe = false;
52388
53016
  const phases = this.buildProtocolV2ExecutionPhases({
52389
53017
  installSources,
52390
53018
  resourceSources,
@@ -52395,10 +53023,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52395
53023
  }
52396
53024
  if (phase.kind === 'resource-sync') {
52397
53025
  yield this.enterProtocolV2BootloaderMode();
53026
+ if (!phase.resourceSources.some(source => isProtocolV2BootResourcePackagePath(source.devicePath))) {
53027
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
53028
+ }
52398
53029
  yield this.executeProtocolV2TransferPhase(phase);
52399
53030
  }
52400
53031
  else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
52401
53032
  yield this.enterProtocolV2BootloaderMode();
53033
+ yield this.ensureProtocolV2BootResourceStagingIsEmpty();
52402
53034
  yield this.executeProtocolV2TransferPhase(phase);
52403
53035
  yield this.exitProtocolV2BootloaderToNormal();
52404
53036
  }
@@ -52410,6 +53042,31 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52410
53042
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 execution has no final verification phase');
52411
53043
  });
52412
53044
  }
53045
+ ensureProtocolV2BootResourceStagingIsEmpty() {
53046
+ var _a, _b;
53047
+ return __awaiter(this, void 0, void 0, function* () {
53048
+ if (this.protocolV2BootResourceStagingSafe)
53049
+ return;
53050
+ const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
53051
+ const query = () => typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
53052
+ path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
53053
+ });
53054
+ const current = yield query();
53055
+ if ((_a = current.message) === null || _a === void 0 ? void 0 : _a.exist) {
53056
+ if (current.message.directory) {
53057
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, 'Protocol V2 boot resource staging path is not a file');
53058
+ }
53059
+ yield typedCall('FilesystemFileDelete', 'Success', {
53060
+ path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
53061
+ });
53062
+ const remaining = yield query();
53063
+ if ((_b = remaining.message) === null || _b === void 0 ? void 0 : _b.exist) {
53064
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, 'Protocol V2 stale boot resource staging file could not be removed');
53065
+ }
53066
+ }
53067
+ this.protocolV2BootResourceStagingSafe = true;
53068
+ });
53069
+ }
52413
53070
  executeProtocolV2SourceUpdate({ installSources, resourceSources, }) {
52414
53071
  return __awaiter(this, void 0, void 0, function* () {
52415
53072
  return this.executeProtocolV2Phases({
@@ -52418,7 +53075,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52418
53075
  });
52419
53076
  });
52420
53077
  }
52421
- executeProtocolV2Update({ fwBinaryMap, bootloaderBinary, installItems, resourceBundles, }) {
53078
+ executeProtocolV2Update({ fwBinaryMap, bootloaderBinary, installItems, }) {
52422
53079
  return __awaiter(this, void 0, void 0, function* () {
52423
53080
  const memoryInstallItems = installItems !== null && installItems !== void 0 ? installItems : this.buildProtocolV2InstallItems({
52424
53081
  fwBinaryMap: fwBinaryMap !== null && fwBinaryMap !== void 0 ? fwBinaryMap : [],
@@ -52433,16 +53090,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52433
53090
  kind: item.kind,
52434
53091
  });
52435
53092
  })));
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
53093
  return yield this.executeProtocolV2Phases({
52444
53094
  installSources,
52445
- resourceSources,
53095
+ resourceSources: [],
52446
53096
  });
52447
53097
  }
52448
53098
  finally {
@@ -52455,7 +53105,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52455
53105
  let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
52456
53106
  const resourcesToSync = [];
52457
53107
  for (const resource of resourceSources) {
52458
- if (yield this.isProtocolV2ResourceBundleUpToDate(resource)) {
53108
+ const requiresFreshStaging = isProtocolV2BootResourcePackagePath(resource.devicePath);
53109
+ if (!requiresFreshStaging && (yield this.isProtocolV2ResourceBundleUpToDate(resource))) {
52459
53110
  Log$6.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
52460
53111
  }
52461
53112
  else {
@@ -52466,13 +53117,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52466
53117
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartTransferData);
52467
53118
  let processedSize = 0;
52468
53119
  for (const resource of resourcesToSync) {
53120
+ const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
52469
53121
  processedSize = yield this.protocolV2SourceUpdateProcess({
52470
53122
  source: resource.source,
52471
- filePath: resource.devicePath,
53123
+ filePath: writePath,
52472
53124
  processedSize,
52473
53125
  totalSize,
52474
53126
  });
52475
- yield this.verifyProtocolV2StagedFile(resource.devicePath, resource.source.size);
53127
+ yield this.verifyProtocolV2StagedFile(writePath, resource.source.size);
53128
+ if (isProtocolV2BootResourcePackagePath(resource.devicePath)) {
53129
+ this.protocolV2BootResourceStagingSafe = true;
53130
+ }
52476
53131
  }
52477
53132
  const stagedInstallTargets = [];
52478
53133
  for (const item of installSources) {
@@ -53462,7 +54117,7 @@ class DeviceUploadWallpaper extends BaseMethod {
53462
54117
  }
53463
54118
 
53464
54119
  const FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE = 60802;
53465
- const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
54120
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1 = 60805;
53466
54121
  const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
53467
54122
  const NFT_UPDATE_MESSAGE_TYPE = 61500;
53468
54123
  class DeviceUploadNft extends BaseMethod {
@@ -53491,7 +54146,7 @@ class DeviceUploadNft extends BaseMethod {
53491
54146
  assertCapabilities() {
53492
54147
  return __awaiter(this, void 0, void 0, function* () {
53493
54148
  const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
53494
- const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
54149
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1);
53495
54150
  const hasPathInfo = supportsProtocolV2Message(protocolInfo, FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE);
53496
54151
  const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
53497
54152
  const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
@@ -53626,6 +54281,8 @@ class FileWrite extends BaseMethod {
53626
54281
 
53627
54282
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
53628
54283
  const PORTFOLIO_CHUNK_SIZE = 2048;
54284
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
54285
+ const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
53629
54286
  class UploadPortfolio extends FileWrite {
53630
54287
  init() {
53631
54288
  const { packageBytes, timeoutMs } = this.payload;
@@ -53639,6 +54296,12 @@ class UploadPortfolio extends FileWrite {
53639
54296
  run: { get: () => super.run }
53640
54297
  });
53641
54298
  return __awaiter(this, void 0, void 0, function* () {
54299
+ const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
54300
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
54301
+ const hasPortfolioUpdate = supportsProtocolV2Message(protocolInfo, PORTFOLIO_UPDATE_MESSAGE_TYPE);
54302
+ if (!hasFileWrite || !hasPortfolioUpdate) {
54303
+ throw hdShared.createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
54304
+ }
53642
54305
  const stagedFile = yield _super.run.call(this);
53643
54306
  this.throwIfAborted();
53644
54307
  yield this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
@@ -65180,11 +65843,14 @@ exports.normalizeSafetyCheckLevel = normalizeSafetyCheckLevel;
65180
65843
  exports.normalizeVersionArray = normalizeVersionArray;
65181
65844
  exports.parseConnectSettings = parseConnectSettings;
65182
65845
  exports.parseMessage = parseMessage;
65846
+ exports.parseProtocolV2ResourceManifest = parseProtocolV2ResourceManifest;
65183
65847
  exports.patchFeatures = patchFeatures;
65184
65848
  exports.preloadSessionCache = preloadSessionCache;
65849
+ exports.prepareFirmwareUpdateV4MemoryHost = prepareFirmwareUpdateV4MemoryHost;
65185
65850
  exports.projectDeviceStateFeatures = projectFeatures;
65186
65851
  exports.registerFirmwareUpdateHostBinding = registerFirmwareUpdateHostBinding;
65187
65852
  exports.safeThrowError = safeThrowError;
65853
+ exports.selectProtocolV2ResourceManifestFiles = selectProtocolV2ResourceManifestFiles;
65188
65854
  exports.setLoggerPostMessage = setLoggerPostMessage;
65189
65855
  exports.supportInputPinOnSoftware = supportInputPinOnSoftware;
65190
65856
  exports.switchTransport = switchTransport;